Adjust global permissions

This commit is contained in:
Johannes Meyer 2021-02-18 08:52:57 +01:00
parent 429a70f05f
commit cc65164a67
23 changed files with 345 additions and 172 deletions

View File

@ -43,7 +43,7 @@ class AccountController extends Controller
$config = Config::app()->getSection('global'); $config = Config::app()->getSection('global');
$user = $this->Auth()->getUser(); $user = $this->Auth()->getUser();
if ($user->getAdditional('backend_type') === 'db') { if ($user->getAdditional('backend_type') === 'db') {
if ($user->can('*') || ! $user->can('no-user/password-change')) { if ($user->can('user/password-change')) {
try { try {
$userBackend = UserBackend::create($user->getAdditional('backend_name')); $userBackend = UserBackend::create($user->getAdditional('backend_name'));
} catch (ConfigurationError $e) { } catch (ConfigurationError $e) {

View File

@ -61,7 +61,7 @@ class AnnouncementsController extends Controller
*/ */
public function newAction() public function newAction()
{ {
$this->assertPermission('admin'); $this->assertPermission('application/announcements');
$form = $this->prepareForm()->add(); $form = $this->prepareForm()->add();
$form->handleRequest(); $form->handleRequest();
@ -73,7 +73,7 @@ class AnnouncementsController extends Controller
*/ */
public function updateAction() public function updateAction()
{ {
$this->assertPermission('admin'); $this->assertPermission('application/announcements');
$form = $this->prepareForm()->edit($this->params->getRequired('id')); $form = $this->prepareForm()->edit($this->params->getRequired('id'));
try { try {
@ -89,7 +89,7 @@ class AnnouncementsController extends Controller
*/ */
public function removeAction() public function removeAction()
{ {
$this->assertPermission('admin'); $this->assertPermission('application/announcements');
$form = $this->prepareForm()->remove($this->params->getRequired('id')); $form = $this->prepareForm()->remove($this->params->getRequired('id'));
try { try {

View File

@ -35,24 +35,33 @@ class ConfigController extends Controller
public function createApplicationTabs() public function createApplicationTabs()
{ {
$tabs = $this->getTabs(); $tabs = $this->getTabs();
$tabs->add('general', array( if ($this->hasPermission('config/general')) {
'title' => $this->translate('Adjust the general configuration of Icinga Web 2'), $tabs->add('general', array(
'label' => $this->translate('General'), 'title' => $this->translate('Adjust the general configuration of Icinga Web 2'),
'url' => 'config/general', 'label' => $this->translate('General'),
'baseTarget' => '_main' 'url' => 'config/general',
)); 'baseTarget' => '_main'
$tabs->add('resource', array( ));
'title' => $this->translate('Configure which resources are being utilized by Icinga Web 2'), }
'label' => $this->translate('Resources'), if ($this->hasPermission('config/resources')) {
'url' => 'config/resource', $tabs->add('resource', array(
'baseTarget' => '_main' 'title' => $this->translate('Configure which resources are being utilized by Icinga Web 2'),
)); 'label' => $this->translate('Resources'),
$tabs->add('authentication', array( 'url' => 'config/resource',
'title' => $this->translate('Configure the user and group backends'), 'baseTarget' => '_main'
'label' => $this->translate('Authentication'), ));
'url' => 'config/userbackend', }
'baseTarget' => '_main' if ($this->hasPermission('config/access-control/users')
)); || $this->hasPermission('config/access-control/groups')
) {
$tabs->add('authentication', array(
'title' => $this->translate('Configure the user and group backends'),
'label' => $this->translate('Access Control Backends'),
'url' => 'config/userbackend',
'baseTarget' => '_main'
));
}
return $tabs; return $tabs;
} }
@ -66,7 +75,15 @@ class ConfigController extends Controller
*/ */
public function indexAction() public function indexAction()
{ {
$this->redirectNow('config/general'); if ($this->hasPermission('config/general')) {
$this->redirectNow('config/general');
} elseif ($this->hasPermission('config/resources')) {
$this->redirectNow('config/resource');
} elseif ($this->hasPermission('config/access-control/*')) {
$this->redirectNow('config/userbackend');
} else {
throw new SecurityException('No permission to configure Icinga Web 2');
}
} }
/** /**
@ -76,7 +93,7 @@ class ConfigController extends Controller
*/ */
public function generalAction() public function generalAction()
{ {
$this->assertPermission('config/application/general'); $this->assertPermission('config/general');
$form = new GeneralConfigForm(); $form = new GeneralConfigForm();
$form->setIniConfig(Config::app()); $form->setIniConfig(Config::app());
$form->handleRequest(); $form->handleRequest();
@ -207,14 +224,17 @@ class ConfigController extends Controller
*/ */
public function userbackendAction() public function userbackendAction()
{ {
$this->assertPermission('config/application/userbackend'); if ($this->hasPermission('config/access-control/users')) {
$this->assertPermission('config/application/usergroupbackend'); $form = new UserBackendReorderForm();
$form = new UserBackendReorderForm(); $form->setIniConfig(Config::app('authentication'));
$form->setIniConfig(Config::app('authentication')); $form->handleRequest();
$form->handleRequest(); $this->view->form = $form;
}
if ($this->hasPermission('config/access-control/groups')) {
$this->view->backendNames = Config::app('groups');
}
$this->view->form = $form;
$this->view->backendNames = Config::app('groups');
$this->createApplicationTabs()->activate('authentication'); $this->createApplicationTabs()->activate('authentication');
$this->view->title = $this->translate('Authentication'); $this->view->title = $this->translate('Authentication');
$this->render('userbackend/reorder'); $this->render('userbackend/reorder');
@ -225,7 +245,7 @@ class ConfigController extends Controller
*/ */
public function createuserbackendAction() public function createuserbackendAction()
{ {
$this->assertPermission('config/application/userbackend'); $this->assertPermission('config/access-control/users');
$form = new UserBackendConfigForm(); $form = new UserBackendConfigForm();
$form $form
->setRedirectUrl('config/userbackend') ->setRedirectUrl('config/userbackend')
@ -238,7 +258,7 @@ class ConfigController extends Controller
try { try {
$form->setResourceConfig(ResourceFactory::getResourceConfigs()); $form->setResourceConfig(ResourceFactory::getResourceConfigs());
} catch (ConfigurationError $e) { } catch (ConfigurationError $e) {
if ($this->hasPermission('config/application/resources')) { if ($this->hasPermission('config/resources')) {
Notification::error($e->getMessage()); Notification::error($e->getMessage());
$this->redirectNow('config/createresource'); $this->redirectNow('config/createresource');
} }
@ -272,7 +292,7 @@ class ConfigController extends Controller
*/ */
public function edituserbackendAction() public function edituserbackendAction()
{ {
$this->assertPermission('config/application/userbackend'); $this->assertPermission('config/access-control/users');
$backendName = $this->params->getRequired('backend'); $backendName = $this->params->getRequired('backend');
$form = new UserBackendConfigForm(); $form = new UserBackendConfigForm();
@ -311,7 +331,7 @@ class ConfigController extends Controller
*/ */
public function removeuserbackendAction() public function removeuserbackendAction()
{ {
$this->assertPermission('config/application/userbackend'); $this->assertPermission('config/access-control/users');
$backendName = $this->params->getRequired('backend'); $backendName = $this->params->getRequired('backend');
$backendForm = new UserBackendConfigForm(); $backendForm = new UserBackendConfigForm();
@ -344,7 +364,7 @@ class ConfigController extends Controller
*/ */
public function resourceAction() public function resourceAction()
{ {
$this->assertPermission('config/application/resources'); $this->assertPermission('config/resources');
$this->view->resources = Config::app('resources', true)->getConfigObject() $this->view->resources = Config::app('resources', true)->getConfigObject()
->setKeyColumn('name') ->setKeyColumn('name')
->select() ->select()
@ -358,7 +378,7 @@ class ConfigController extends Controller
*/ */
public function createresourceAction() public function createresourceAction()
{ {
$this->assertPermission('config/application/resources'); $this->assertPermission('config/resources');
$this->getTabs()->add('resources/new', array( $this->getTabs()->add('resources/new', array(
'label' => $this->translate('New Resource'), 'label' => $this->translate('New Resource'),
'url' => Url::fromRequest() 'url' => Url::fromRequest()
@ -379,7 +399,7 @@ class ConfigController extends Controller
*/ */
public function editresourceAction() public function editresourceAction()
{ {
$this->assertPermission('config/application/resources'); $this->assertPermission('config/resources');
$this->getTabs()->add('resources/update', array( $this->getTabs()->add('resources/update', array(
'label' => $this->translate('Update Resource'), 'label' => $this->translate('Update Resource'),
'url' => Url::fromRequest() 'url' => Url::fromRequest()
@ -399,7 +419,7 @@ class ConfigController extends Controller
*/ */
public function removeresourceAction() public function removeresourceAction()
{ {
$this->assertPermission('config/application/resources'); $this->assertPermission('config/resources');
$this->getTabs()->add('resources/remove', array( $this->getTabs()->add('resources/remove', array(
'label' => $this->translate('Remove Resource'), 'label' => $this->translate('Remove Resource'),
'url' => Url::fromRequest() 'url' => Url::fromRequest()

View File

@ -33,7 +33,7 @@ class GroupController extends AuthBackendController
*/ */
public function listAction() public function listAction()
{ {
$this->assertPermission('config/authentication/groups/show'); $this->assertPermission('config/access-control/groups');
$this->createListTabs()->activate('group/list'); $this->createListTabs()->activate('group/list');
$backendNames = array_map( $backendNames = array_map(
function ($b) { function ($b) {
@ -90,7 +90,7 @@ class GroupController extends AuthBackendController
*/ */
public function showAction() public function showAction()
{ {
$this->assertPermission('config/authentication/groups/show'); $this->assertPermission('config/access-control/groups');
$groupName = $this->params->getRequired('group'); $groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend')); $backend = $this->getUserGroupBackend($this->params->getRequired('backend'));
@ -125,7 +125,7 @@ class GroupController extends AuthBackendController
$this->view->members = $members; $this->view->members = $members;
$this->createShowTabs($backend->getName(), $groupName)->activate('group/show'); $this->createShowTabs($backend->getName(), $groupName)->activate('group/show');
if ($this->hasPermission('config/authentication/groups/edit') && $backend instanceof Reducible) { if ($this->hasPermission('config/access-control/groups') && $backend instanceof Reducible) {
$removeForm = new Form(); $removeForm = new Form();
$removeForm->setUidDisabled(); $removeForm->setUidDisabled();
$removeForm->setAttrib('class', 'inline'); $removeForm->setAttrib('class', 'inline');
@ -161,7 +161,7 @@ class GroupController extends AuthBackendController
*/ */
public function addAction() public function addAction()
{ {
$this->assertPermission('config/authentication/groups/add'); $this->assertPermission('config/access-control/groups');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible'); $backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible');
$form = new UserGroupForm(); $form = new UserGroupForm();
$form->setRedirectUrl(Url::fromPath('group/list', array('backend' => $backend->getName()))); $form->setRedirectUrl(Url::fromPath('group/list', array('backend' => $backend->getName())));
@ -176,7 +176,7 @@ class GroupController extends AuthBackendController
*/ */
public function editAction() public function editAction()
{ {
$this->assertPermission('config/authentication/groups/edit'); $this->assertPermission('config/access-control/groups');
$groupName = $this->params->getRequired('group'); $groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Updatable'); $backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Updatable');
@ -200,7 +200,7 @@ class GroupController extends AuthBackendController
*/ */
public function removeAction() public function removeAction()
{ {
$this->assertPermission('config/authentication/groups/remove'); $this->assertPermission('config/access-control/groups');
$groupName = $this->params->getRequired('group'); $groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible'); $backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible');
@ -222,7 +222,7 @@ class GroupController extends AuthBackendController
*/ */
public function addmemberAction() public function addmemberAction()
{ {
$this->assertPermission('config/authentication/groups/edit'); $this->assertPermission('config/access-control/groups');
$groupName = $this->params->getRequired('group'); $groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible'); $backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible');
@ -249,7 +249,7 @@ class GroupController extends AuthBackendController
*/ */
public function removememberAction() public function removememberAction()
{ {
$this->assertPermission('config/authentication/groups/edit'); $this->assertPermission('config/access-control/groups');
$this->assertHttpMethod('POST'); $this->assertHttpMethod('POST');
$groupName = $this->params->getRequired('group'); $groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible'); $backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible');
@ -368,26 +368,32 @@ class GroupController extends AuthBackendController
protected function createListTabs() protected function createListTabs()
{ {
$tabs = $this->getTabs(); $tabs = $this->getTabs();
$tabs->add(
'role/list',
array(
'baseTarget' => '_main',
'label' => $this->translate('Roles'),
'title' => $this->translate(
'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
),
'url' => 'role/list'
) if ($this->hasPermission('config/access-control/roles')) {
); $tabs->add(
$tabs->add( 'role/list',
'user/list', array(
array( 'baseTarget' => '_main',
'title' => $this->translate('List users of authentication backends'), 'label' => $this->translate('Roles'),
'label' => $this->translate('Users'), 'title' => $this->translate(
'url' => 'user/list' 'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
) ),
); 'url' => 'role/list'
)
);
}
if ($this->hasPermission('config/access-control/users')) {
$tabs->add(
'user/list',
array(
'title' => $this->translate('List users of authentication backends'),
'label' => $this->translate('Users'),
'url' => 'user/list'
)
);
}
$tabs->add( $tabs->add(
'group/list', 'group/list',
array( array(
@ -396,6 +402,7 @@ class GroupController extends AuthBackendController
'url' => 'group/list' 'url' => 'group/list'
) )
); );
return $tabs; return $tabs;
} }
} }

View File

@ -161,7 +161,7 @@ class NavigationController extends Controller
*/ */
public function sharedAction() public function sharedAction()
{ {
$this->assertPermission('config/application/navigation'); $this->assertPermission('config/navigation');
$ds = new ArrayDatasource($this->fetchSharedNavigationItemConfigs()); $ds = new ArrayDatasource($this->fetchSharedNavigationItemConfigs());
$query = $ds->select(); $query = $ds->select();
@ -259,7 +259,7 @@ class NavigationController extends Controller
$referrer = $this->params->get('referrer', 'index'); $referrer = $this->params->get('referrer', 'index');
$user = $this->Auth()->getUser(); $user = $this->Auth()->getUser();
if ($user->can('config/application/navigation')) { if ($user->can('config/navigation')) {
$itemOwner = $this->params->get('owner', $user->getUsername()); $itemOwner = $this->params->get('owner', $user->getUsername());
} else { } else {
$itemOwner = $user->getUsername(); $itemOwner = $user->getUsername();
@ -354,7 +354,7 @@ class NavigationController extends Controller
*/ */
public function unshareAction() public function unshareAction()
{ {
$this->assertPermission('config/application/navigation'); $this->assertPermission('config/navigation');
$this->assertHttpMethod('POST'); $this->assertHttpMethod('POST');
// TODO: I'd like these being form fields // TODO: I'd like these being form fields

View File

@ -6,6 +6,7 @@ namespace Icinga\Controllers;
use Icinga\Authentication\RolesConfig; use Icinga\Authentication\RolesConfig;
use Icinga\Exception\NotFoundError; use Icinga\Exception\NotFoundError;
use Icinga\Forms\Security\RoleForm; use Icinga\Forms\Security\RoleForm;
use Icinga\Security\SecurityException;
use Icinga\Web\Controller\AuthBackendController; use Icinga\Web\Controller\AuthBackendController;
/** /**
@ -22,6 +23,19 @@ class RoleController extends AuthBackendController
parent::init(); parent::init();
} }
public function indexAction()
{
if ($this->hasPermission('config/access-control/roles')) {
$this->redirectNow('role/list');
} elseif ($this->hasPermission('config/access-control/users')) {
$this->redirectNow('user/list');
} elseif ($this->hasPermission('config/access-control/groups')) {
$this->redirectNow('group/list');
} else {
throw new SecurityException('No permission to configure Icinga Web 2');
}
}
/** /**
* List roles * List roles
* *
@ -29,7 +43,7 @@ class RoleController extends AuthBackendController
*/ */
public function listAction() public function listAction()
{ {
$this->assertPermission('config/authentication/roles/show'); $this->assertPermission('config/access-control/roles');
$this->createListTabs()->activate('role/list'); $this->createListTabs()->activate('role/list');
$this->view->roles = (new RolesConfig()) $this->view->roles = (new RolesConfig())
->select(); ->select();
@ -54,7 +68,7 @@ class RoleController extends AuthBackendController
*/ */
public function addAction() public function addAction()
{ {
$this->assertPermission('config/authentication/roles/add'); $this->assertPermission('config/access-control/roles');
$role = new RoleForm(); $role = new RoleForm();
$role->setRedirectUrl('role/list'); $role->setRedirectUrl('role/list');
@ -72,7 +86,7 @@ class RoleController extends AuthBackendController
*/ */
public function editAction() public function editAction()
{ {
$this->assertPermission('config/authentication/roles/edit'); $this->assertPermission('config/access-control/roles');
$name = $this->params->getRequired('role'); $name = $this->params->getRequired('role');
$role = new RoleForm(); $role = new RoleForm();
@ -95,7 +109,7 @@ class RoleController extends AuthBackendController
*/ */
public function removeAction() public function removeAction()
{ {
$this->assertPermission('config/authentication/roles/remove'); $this->assertPermission('config/access-control/roles');
$name = $this->params->getRequired('role'); $name = $this->params->getRequired('role');
$role = new RoleForm(); $role = new RoleForm();
@ -128,25 +142,31 @@ class RoleController extends AuthBackendController
'Configure roles to permit or restrict users and groups accessing Icinga Web 2' 'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
), ),
'url' => 'role/list' 'url' => 'role/list'
)
);
if ($this->hasPermission('config/access-control/users')) {
$tabs->add(
'user/list',
array(
'title' => $this->translate('List users of authentication backends'),
'label' => $this->translate('Users'),
'url' => 'user/list'
)
);
}
if ($this->hasPermission('config/access-control/groups')) {
$tabs->add(
'group/list',
array(
'title' => $this->translate('List groups of user group backends'),
'label' => $this->translate('User Groups'),
'url' => 'group/list'
)
);
}
)
);
$tabs->add(
'user/list',
array(
'title' => $this->translate('List users of authentication backends'),
'label' => $this->translate('Users'),
'url' => 'user/list'
)
);
$tabs->add(
'group/list',
array(
'title' => $this->translate('List groups of user group backends'),
'label' => $this->translate('User Groups'),
'url' => 'group/list'
)
);
return $tabs; return $tabs;
} }
} }

View File

@ -33,7 +33,7 @@ class UserController extends AuthBackendController
*/ */
public function listAction() public function listAction()
{ {
$this->assertPermission('config/authentication/users/show'); $this->assertPermission('config/access-control/users');
$this->createListTabs()->activate('user/list'); $this->createListTabs()->activate('user/list');
$backendNames = array_map( $backendNames = array_map(
function ($b) { function ($b) {
@ -91,7 +91,7 @@ class UserController extends AuthBackendController
*/ */
public function showAction() public function showAction()
{ {
$this->assertPermission('config/authentication/users/show'); $this->assertPermission('config/access-control/users');
$userName = $this->params->getRequired('user'); $userName = $this->params->getRequired('user');
$backend = $this->getUserBackend($this->params->getRequired('backend')); $backend = $this->getUserBackend($this->params->getRequired('backend'));
@ -127,7 +127,7 @@ class UserController extends AuthBackendController
$memberships $memberships
); );
if ($this->hasPermission('config/authentication/groups/edit')) { if ($this->hasPermission('config/access-control/groups')) {
$extensibleBackends = $this->loadUserGroupBackends('Icinga\Data\Extensible'); $extensibleBackends = $this->loadUserGroupBackends('Icinga\Data\Extensible');
$this->view->showCreateMembershipLink = ! empty($extensibleBackends); $this->view->showCreateMembershipLink = ! empty($extensibleBackends);
} else { } else {
@ -139,7 +139,7 @@ class UserController extends AuthBackendController
$this->view->memberships = $memberships; $this->view->memberships = $memberships;
$this->createShowTabs($backend->getName(), $userName)->activate('user/show'); $this->createShowTabs($backend->getName(), $userName)->activate('user/show');
if ($this->hasPermission('config/authentication/groups/edit')) { if ($this->hasPermission('config/access-control/groups')) {
$removeForm = new Form(); $removeForm = new Form();
$removeForm->setUidDisabled(); $removeForm->setUidDisabled();
$removeForm->setAttrib('class', 'inline'); $removeForm->setAttrib('class', 'inline');
@ -170,7 +170,7 @@ class UserController extends AuthBackendController
$admissionLoader = new AdmissionLoader(); $admissionLoader = new AdmissionLoader();
$admissionLoader->applyRoles($userObj); $admissionLoader->applyRoles($userObj);
$this->view->userObj = $userObj; $this->view->userObj = $userObj;
$this->view->allowedToEditRoles = $this->hasPermission('config/authentication/roles/edit'); $this->view->allowedToEditRoles = $this->hasPermission('config/access-control/groups');
} }
/** /**
@ -178,7 +178,7 @@ class UserController extends AuthBackendController
*/ */
public function addAction() public function addAction()
{ {
$this->assertPermission('config/authentication/users/add'); $this->assertPermission('config/access-control/users');
$backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible'); $backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible');
$form = new UserForm(); $form = new UserForm();
$form->setRedirectUrl(Url::fromPath('user/list', array('backend' => $backend->getName()))); $form->setRedirectUrl(Url::fromPath('user/list', array('backend' => $backend->getName())));
@ -193,7 +193,7 @@ class UserController extends AuthBackendController
*/ */
public function editAction() public function editAction()
{ {
$this->assertPermission('config/authentication/users/edit'); $this->assertPermission('config/access-control/users');
$userName = $this->params->getRequired('user'); $userName = $this->params->getRequired('user');
$backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Updatable'); $backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Updatable');
@ -215,7 +215,7 @@ class UserController extends AuthBackendController
*/ */
public function removeAction() public function removeAction()
{ {
$this->assertPermission('config/authentication/users/remove'); $this->assertPermission('config/access-control/users');
$userName = $this->params->getRequired('user'); $userName = $this->params->getRequired('user');
$backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible'); $backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible');
@ -237,7 +237,7 @@ class UserController extends AuthBackendController
*/ */
public function createmembershipAction() public function createmembershipAction()
{ {
$this->assertPermission('config/authentication/groups/edit'); $this->assertPermission('config/access-control/groups');
$userName = $this->params->getRequired('user'); $userName = $this->params->getRequired('user');
$backend = $this->getUserBackend($this->params->getRequired('backend')); $backend = $this->getUserBackend($this->params->getRequired('backend'));
@ -325,18 +325,21 @@ class UserController extends AuthBackendController
protected function createListTabs() protected function createListTabs()
{ {
$tabs = $this->getTabs(); $tabs = $this->getTabs();
$tabs->add(
'role/list',
array(
'baseTarget' => '_main',
'label' => $this->translate('Roles'),
'title' => $this->translate(
'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
),
'url' => 'role/list'
) if ($this->hasPermission('config/access-control/roles')) {
); $tabs->add(
'role/list',
array(
'baseTarget' => '_main',
'label' => $this->translate('Roles'),
'title' => $this->translate(
'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
),
'url' => 'role/list'
)
);
}
$tabs->add( $tabs->add(
'user/list', 'user/list',
array( array(
@ -345,14 +348,18 @@ class UserController extends AuthBackendController
'url' => 'user/list' 'url' => 'user/list'
) )
); );
$tabs->add(
'group/list', if ($this->hasPermission('config/access-control/groups')) {
array( $tabs->add(
'title' => $this->translate('List groups of user group backends'), 'group/list',
'label' => $this->translate('User Groups'), array(
'url' => 'group/list' 'title' => $this->translate('List groups of user group backends'),
) 'label' => $this->translate('User Groups'),
); 'url' => 'group/list'
)
);
}
return $tabs; return $tabs;
} }
} }

View File

@ -21,7 +21,7 @@ class UsergroupbackendController extends Controller
*/ */
public function init() public function init()
{ {
$this->assertPermission('config/application/usergroupbackend'); $this->assertPermission('config/access-control/users');
} }
/** /**

View File

@ -299,7 +299,7 @@ class NavigationConfigForm extends ConfigForm
$shared = false; $shared = false;
$config = $this->getUserConfig($data['type']); $config = $this->getUserConfig($data['type']);
if ((isset($data['users']) && $data['users']) || (isset($data['groups']) && $data['groups'])) { if ((isset($data['users']) && $data['users']) || (isset($data['groups']) && $data['groups'])) {
if ($this->getUser()->can('application/share/navigation')) { if ($this->getUser()->can('user/share/navigation')) {
$data['owner'] = $this->getUser()->getUsername(); $data['owner'] = $this->getUser()->getUsername();
$config = $this->getShareConfig($data['type']); $config = $this->getShareConfig($data['type']);
$shared = true; $shared = true;
@ -370,7 +370,7 @@ class NavigationConfigForm extends ConfigForm
$config = $this->unshare($name, isset($data['parent']) ? $data['parent'] : null); $config = $this->unshare($name, isset($data['parent']) ? $data['parent'] : null);
} }
} elseif ((isset($data['users']) && $data['users']) || (isset($data['groups']) && $data['groups'])) { } elseif ((isset($data['users']) && $data['users']) || (isset($data['groups']) && $data['groups'])) {
if ($this->getUser()->can('application/share/navigation')) { if ($this->getUser()->can('user/share/navigation')) {
// It is not shared yet but should be // It is not shared yet but should be
$this->secondaryConfig = $config; $this->secondaryConfig = $config;
$config = $this->getShareConfig(); $config = $this->getShareConfig();
@ -580,7 +580,7 @@ class NavigationConfigForm extends ConfigForm
); );
if ((! $itemForm->requiresParentSelection() || ! isset($formData['parent']) || ! $formData['parent']) if ((! $itemForm->requiresParentSelection() || ! isset($formData['parent']) || ! $formData['parent'])
&& $this->getUser()->can('application/share/navigation') && $this->getUser()->can('user/share/navigation')
) { ) {
$checked = isset($formData['shared']) ? null : (isset($formData['users']) || isset($formData['groups'])); $checked = isset($formData['shared']) ? null : (isset($formData['users']) || isset($formData['groups']));
@ -783,7 +783,7 @@ class NavigationConfigForm extends ConfigForm
return $this->getUserConfig(); return $this->getUserConfig();
} elseif ($this->getShareConfig()->hasSection($name)) { } elseif ($this->getShareConfig()->hasSection($name)) {
if ($this->getShareConfig()->get($name, 'owner') === $this->getUser()->getUsername() if ($this->getShareConfig()->get($name, 'owner') === $this->getUser()->getUsername()
|| $this->getUser()->can('config/application/navigation') || $this->getUser()->can('user/share/navigation')
) { ) {
return $this->getShareConfig(); return $this->getShareConfig();
} }

View File

@ -255,7 +255,7 @@ class PreferenceForm extends Form
) )
); );
if (Auth::getInstance()->hasPermission('application/stacktraces')) { if (Auth::getInstance()->hasPermission('user/application/stacktraces')) {
$this->addElement( $this->addElement(
'checkbox', 'checkbox',
'show_stacktraces', 'show_stacktraces',

View File

@ -6,6 +6,7 @@ namespace Icinga\Forms\Security;
use Icinga\Application\Hook\ConfigFormEventsHook; use Icinga\Application\Hook\ConfigFormEventsHook;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Modules\Manager; use Icinga\Application\Modules\Manager;
use Icinga\Authentication\AdmissionLoader;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Forms\ConfigForm; use Icinga\Forms\ConfigForm;
use Icinga\Forms\RepositoryForm; use Icinga\Forms\RepositoryForm;
@ -50,33 +51,67 @@ class RoleForm extends RepositoryForm
$view = $this->getView(); $view = $this->getView();
$this->providedPermissions['application'] = [ $this->providedPermissions['application'] = [
$helper->filterName('no-user/password-change') => [ $helper->filterName('application/announcements') => [
'name' => 'no-user/password-change', 'name' => 'application/announcements',
'description' => $this->translate('Prohibit password changes in the account preferences') 'description' => $this->translate('Allow to manage announcements')
],
$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') => [ $helper->filterName('application/log') => [
'name' => 'application/log', 'name' => 'application/log',
'description' => $this->translate('Allow to view the 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/*') => [ $helper->filterName('config/*') => [
'name' => 'config/*', 'name' => 'config/*',
'description' => $this->translate('Allow config access') 'description' => $this->translate('Allow full config access')
],
$helper->filterName('config/general') => [
'name' => 'config/general',
'description' => $this->translate('Allow to adjust the general configuration')
],
$helper->filterName('config/modules') => [
'name' => 'config/modules',
'description' => $this->translate('Allow to enable/disable and configure modules')
],
$helper->filterName('config/resources') => [
'name' => 'config/resources',
'description' => $this->translate('Allow to manage resources')
],
$helper->filterName('config/navigation') => [
'name' => 'config/navigation',
'description' => $this->translate('Allow to view and adjust shared navigation items')
],
$helper->filterName('config/access-control/*') => [
'name' => 'config/access-control/*',
'description' => $this->translate('Allow to fully manage access-control')
],
$helper->filterName('config/access-control/users') => [
'name' => 'config/access-control/users',
'description' => $this->translate('Allow to manage user accounts')
],
$helper->filterName('config/access-control/groups') => [
'name' => 'config/access-control/groups',
'description' => $this->translate('Allow to manage user groups')
],
$helper->filterName('config/access-control/roles') => [
'name' => 'config/access-control/roles',
'description' => $this->translate('Allow to manage roles')
],
$helper->filterName('user/*') => [
'name' => 'user/*',
'description' => $this->translate('Allow all account related functionalities')
],
$helper->filterName('user/password-change') => [
'name' => 'user/password-change',
'description' => $this->translate('Allow password changes in the account preferences')
],
$helper->filterName('user/application/stacktraces') => [
'name' => 'user/application/stacktraces',
'description' => $this->translate(
'Allow to adjust in the preferences whether to show stacktraces'
)
],
$helper->filterName('user/share/navigation') => [
'name' => 'user/share/navigation',
'description' => $this->translate('Allow to share navigation items')
] ]
]; ];
@ -359,6 +394,12 @@ class RoleForm extends RepositoryForm
if (! empty($role->permissions) || ! empty($role->refusals)) { if (! empty($role->permissions) || ! empty($role->refusals)) {
$permissions = StringHelper::trimSplit($role->permissions); $permissions = StringHelper::trimSplit($role->permissions);
$refusals = StringHelper::trimSplit($role->refusals); $refusals = StringHelper::trimSplit($role->refusals);
list($permissions, $newRefusals) = AdmissionLoader::migrateLegacyPermissions($permissions);
if (! empty($newRefusals)) {
array_push($refusals, ...$newRefusals);
}
foreach ($this->providedPermissions as $moduleName => $permissionList) { foreach ($this->providedPermissions as $moduleName => $permissionList) {
foreach ($permissionList as $name => $spec) { foreach ($permissionList as $name => $spec) {
if (in_array($spec['name'], $permissions, true)) { if (in_array($spec['name'], $permissions, true)) {

View File

@ -10,7 +10,7 @@
</div> </div>
<?php endif ?> <?php endif ?>
<div class="content"> <div class="content">
<?php if ($this->hasPermission('admin')) { <?php if ($this->hasPermission('application/announcements')) {
echo $this->qlink( echo $this->qlink(
$this->translate('Create a New Announcement') , $this->translate('Create a New Announcement') ,
'announcements/new', 'announcements/new',
@ -41,7 +41,7 @@
<?php foreach ($this->announcements as $announcement): /** @var object $announcement */ ?> <?php foreach ($this->announcements as $announcement): /** @var object $announcement */ ?>
<tr> <tr>
<td><?= $this->escape($announcement->author) ?></td> <td><?= $this->escape($announcement->author) ?></td>
<?php if ($this->hasPermission('admin')): ?> <?php if ($this->hasPermission('application/announcements')): ?>
<td> <td>
<a href="<?= $this->href('announcements/update', array('id' => $announcement->id)) ?>"> <a href="<?= $this->href('announcements/update', array('id' => $announcement->id)) ?>">
<?= $this->ellipsis($this->escape($announcement->message), 100) ?> <?= $this->ellipsis($this->escape($announcement->message), 100) ?>
@ -52,7 +52,7 @@
<?php endif ?> <?php endif ?>
<td><?= $this->formatDateTime($announcement->start) ?></td> <td><?= $this->formatDateTime($announcement->start) ?></td>
<td><?= $this->formatDateTime($announcement->end) ?></td> <td><?= $this->formatDateTime($announcement->end) ?></td>
<?php if ($this->hasPermission('admin')): ?> <?php if ($this->hasPermission('application/announcements')): ?>
<td class="icon-col"><?= $this->qlink( <td class="icon-col"><?= $this->qlink(
null, null,
'announcements/remove', 'announcements/remove',

View File

@ -2,6 +2,7 @@
<?= $tabs ?> <?= $tabs ?>
</div> </div>
<div class="content"> <div class="content">
<?php if ($this->auth()->hasPermission('config/access-control/users')): ?>
<h1><?= $this->translate('User Backends') ?></h1> <h1><?= $this->translate('User Backends') ?></h1>
<?= $this->qlink( <?= $this->qlink(
$this->translate('Create a New User Backend') , $this->translate('Create a New User Backend') ,
@ -15,7 +16,9 @@
) )
) ?> ) ?>
<?= $form ?> <?= $form ?>
<?php endif ?>
<?php if ($this->auth()->hasPermission('config/access-control/groups')): ?>
<h1><?= $this->translate('User Group Backends') ?></h1> <h1><?= $this->translate('User Group Backends') ?></h1>
<?= $this->qlink( <?= $this->qlink(
$this->translate('Create a New User Group Backend') , $this->translate('Create a New User Group Backend') ,
@ -68,4 +71,5 @@
<?php endforeach ?> <?php endforeach ?>
</tbody> </tbody>
</table> </table>
<?php endif ?>
</div> </div>

View File

@ -22,8 +22,8 @@ if (! isset($backend)) {
echo $this->translate('No backend found which is able to list user groups') . '</div>'; echo $this->translate('No backend found which is able to list user groups') . '</div>';
return; return;
} else { } else {
$extensible = $this->hasPermission('config/authentication/groups/add') && $backend instanceof Extensible; $extensible = $this->hasPermission('config/access-control/groups') && $backend instanceof Extensible;
$reducible = $this->hasPermission('config/authentication/groups/remove') && $backend instanceof Reducible; $reducible = $this->hasPermission('config/access-control/groups') && $backend instanceof Reducible;
} }
?> ?>

View File

@ -3,10 +3,10 @@
use Icinga\Data\Extensible; use Icinga\Data\Extensible;
use Icinga\Data\Updatable; use Icinga\Data\Updatable;
$extensible = $this->hasPermission('config/authentication/groups/add') && $backend instanceof Extensible; $extensible = $this->hasPermission('config/access-control/groups') && $backend instanceof Extensible;
$editLink = null; $editLink = null;
if ($this->hasPermission('config/authentication/groups/edit') && $backend instanceof Updatable) { if ($this->hasPermission('config/access-control/groups') && $backend instanceof Updatable) {
$editLink = $this->qlink( $editLink = $this->qlink(
null, null,
'group/edit', 'group/edit',
@ -83,7 +83,7 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan
<tr> <tr>
<td> <td>
<?php if ( <?php if (
$this->hasPermission('config/authentication/users/show') $this->hasPermission('config/access-control/users')
&& ($userBackend = $backend->getUserBackendName($member->user_name)) !== null && ($userBackend = $backend->getUserBackendName($member->user_name)) !== null
): ?> ): ?>
<?= $this->qlink($member->user_name, 'user/show', array( <?= $this->qlink($member->user_name, 'user/show', array(

View File

@ -22,8 +22,8 @@ if (! isset($backend)) {
echo $this->translate('No backend found which is able to list users') . '</div>'; echo $this->translate('No backend found which is able to list users') . '</div>';
return; return;
} else { } else {
$extensible = $this->hasPermission('config/authentication/users/add') && $backend instanceof Extensible; $extensible = $this->hasPermission('config/access-control/users') && $backend instanceof Extensible;
$reducible = $this->hasPermission('config/authentication/users/remove') && $backend instanceof Reducible; $reducible = $this->hasPermission('config/access-control/users') && $backend instanceof Reducible;
} }
?> ?>

View File

@ -11,7 +11,7 @@ use Icinga\Data\Selectable;
<?php endif ?> <?php endif ?>
<h2><?= $this->escape($user->user_name) ?></h2> <h2><?= $this->escape($user->user_name) ?></h2>
<?php <?php
if ($this->hasPermission('config/authentication/users/edit') && $backend instanceof Updatable) { if ($this->hasPermission('config/access-control/users') && $backend instanceof Updatable) {
echo $this->qlink( echo $this->qlink(
$this->translate('Edit User'), $this->translate('Edit User'),
'user/edit', 'user/edit',
@ -110,7 +110,7 @@ use Icinga\Data\Selectable;
<?php foreach ($memberships as $membership): ?> <?php foreach ($memberships as $membership): ?>
<tr> <tr>
<td> <td>
<?php if ($this->hasPermission('config/authentication/groups/show') && $membership->backend instanceof Selectable): ?> <?php if ($this->hasPermission('config/access-control/groups') && $membership->backend instanceof Selectable): ?>
<?= $this->qlink($membership->group_name, 'group/show', array( <?= $this->qlink($membership->group_name, 'group/show', array(
'backend' => $membership->backend->getName(), 'backend' => $membership->backend->getName(),
'group' => $membership->group_name 'group' => $membership->group_name

View File

@ -110,12 +110,25 @@ a module permission in the format `module/<moduleName>` for each installed modul
### General Permissions ### General Permissions
Name | Permits Name | Permits
--------------------------|----------------------------------------------- -----------------------------|-----------------------------------------------
\* | allow everything, including module-specific permissions \* | allow everything, including module-specific permissions
config/\* | allow all configuration actions application/announcements | allow to manage announcements
config/modules | allow enabling or disabling modules application/log | allow to view the application log
module/`<moduleName>` | allow access to module `<moduleName>` (e.g. `module/monitoring`) config/\* | allow full config access
config/access-control/\* | allow to fully manage access control
config/access-control/groups | allow to manage groups
config/access-control/roles | allow to manage roles
config/access-control/users | allow to manage user accounts
config/general | allow to adjust the general configuration
config/modules | allow to enable/disable and configure modules
config/navigation | allow to view and adjust shared navigation items
config/resources | allow to manage resources
user/\* | allow all account related functionalities
user/application/stacktraces | allow to adjust in the preferences whether to show stacktraces
user/password-change | allow password changes in the account preferences
user/share/navigation | allow to share navigation items
module/`<moduleName>` | allow access to module `<moduleName>` (e.g. `module/monitoring`)
### Monitoring Module Permissions ### Monitoring Module Permissions

View File

@ -336,7 +336,7 @@ class Web extends EmbeddedWeb
$this->getRequest()->setUser($user); $this->getRequest()->setUser($user);
$this->user = $user; $this->user = $user;
if ($user->can('application/stacktraces')) { if ($user->can('user/application/stacktraces')) {
$displayExceptions = $this->user->getPreferences()->getValue( $displayExceptions = $this->user->getPreferences()->getValue(
'icingaweb', 'icingaweb',
'show_stacktraces' 'show_stacktraces'

View File

@ -17,6 +17,35 @@ use Icinga\Util\StringHelper;
*/ */
class AdmissionLoader class AdmissionLoader
{ {
const LEGACY_PERMISSIONS = [
'admin' => 'application/announcements',
'application/stacktraces' => 'user/application/stacktraces',
'application/share/navigation' => 'user/share/navigation',
// Migrating config/application/* would include config/modules, so that's skipped
//'config/application/*' => 'config/*',
'config/application/general' => 'config/general',
'config/application/resources' => 'config/resources',
'config/application/navigation' => 'config/navigation',
'config/application/userbackend' => 'config/access-control/users',
'config/application/usergroupbackend' => 'config/access-control/groups',
'config/authentication/*' => 'config/access-control/*',
'config/authentication/users/*' => 'config/access-control/users',
'config/authentication/users/show' => 'config/access-control/users',
'config/authentication/users/add' => 'config/access-control/users',
'config/authentication/users/edit' => 'config/access-control/users',
'config/authentication/users/remove' => 'config/access-control/users',
'config/authentication/groups/*' => 'config/access-control/groups',
'config/authentication/groups/show' => 'config/access-control/groups',
'config/authentication/groups/edit' => 'config/access-control/groups',
'config/authentication/groups/add' => 'config/access-control/groups',
'config/authentication/groups/remove' => 'config/access-control/groups',
'config/authentication/roles/*' => 'config/access-control/roles',
'config/authentication/roles/show' => 'config/access-control/roles',
'config/authentication/roles/add' => 'config/access-control/roles',
'config/authentication/roles/edit' => 'config/access-control/roles',
'config/authentication/roles/remove' => 'config/access-control/roles'
];
/** @var Role[] */ /** @var Role[] */
protected $roles; protected $roles;
@ -84,6 +113,11 @@ class AdmissionLoader
$permissions = StringHelper::trimSplit($section->permissions); $permissions = StringHelper::trimSplit($section->permissions);
$refusals = StringHelper::trimSplit($section->refusals); $refusals = StringHelper::trimSplit($section->refusals);
list($permissions, $newRefusals) = self::migrateLegacyPermissions($permissions);
if (! empty($newRefusals)) {
array_push($refusals, ...$newRefusals);
}
$restrictions = $section->toArray(); $restrictions = $section->toArray();
unset($restrictions['users'], $restrictions['groups']); unset($restrictions['users'], $restrictions['groups']);
unset($restrictions['refusals'], $restrictions['permissions']); unset($restrictions['refusals'], $restrictions['permissions']);
@ -176,4 +210,22 @@ class AdmissionLoader
$user->setPermissions($permissions); $user->setPermissions($permissions);
$user->setRoles(array_values($roles)); $user->setRoles(array_values($roles));
} }
public static function migrateLegacyPermissions(array $permissions)
{
$migratedGrants = [];
$refusals = [];
foreach ($permissions as $permission) {
if (array_key_exists($permission, self::LEGACY_PERMISSIONS)) {
$migratedGrants[] = self::LEGACY_PERMISSIONS[$permission];
} elseif ($permission === 'no-user/password-change') {
$refusals[] = 'user/password-change';
} else {
$migratedGrants[] = $permission;
}
}
return [$migratedGrants, $refusals];
}
} }

View File

@ -4,6 +4,7 @@
namespace Icinga; namespace Icinga;
use DateTimeZone; use DateTimeZone;
use Icinga\Authentication\AdmissionLoader;
use InvalidArgumentException; use InvalidArgumentException;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Authentication\Role; use Icinga\Authentication\Role;
@ -559,6 +560,15 @@ class User
*/ */
public function can($requiredPermission) public function can($requiredPermission)
{ {
list($permissions, $refusals) = AdmissionLoader::migrateLegacyPermissions([$requiredPermission]);
if (! empty($permissions)) {
$requiredPermission = array_pop($permissions);
} elseif (! empty($refusals)) {
throw new InvalidArgumentException(
'Refusals are not supported anymore. Check for a grant instead!'
);
}
$granted = false; $granted = false;
foreach ($this->getRoles() as $role) { foreach ($this->getRoles() as $role) {
if ($role->denies($requiredPermission)) { if ($role->denies($requiredPermission)) {

View File

@ -67,24 +67,23 @@ class Menu extends Navigation
'icon' => 'wrench', 'icon' => 'wrench',
'description' => t('Open application configuration'), 'description' => t('Open application configuration'),
'label' => t('Application'), 'label' => t('Application'),
'url' => 'config/general', 'url' => 'config',
'permission' => 'config/application/*',
'priority' => 810 'priority' => 810
], ],
'authentication' => [ 'authentication' => [
'icon' => 'users', 'icon' => 'users',
'description' => t('Open authentication configuration'), 'description' => t('Open access control configuration'),
'label' => t('Authentication'), 'label' => t('Access Control'),
'permission' => 'config/authentication/*', 'permission' => 'config/access-control/*',
'priority' => 830, 'priority' => 830,
'url' => 'role/list' 'url' => 'role'
], ],
'navigation' => [ 'navigation' => [
'icon' => 'sitemap', 'icon' => 'sitemap',
'description' => t('Open shared navigation configuration'), 'description' => t('Open shared navigation configuration'),
'label' => t('Shared Navigation'), 'label' => t('Shared Navigation'),
'url' => 'navigation/shared', 'url' => 'navigation/shared',
'permission' => 'config/application/navigation', 'permission' => 'config/navigation',
'priority' => 840, 'priority' => 840,
], ],
'modules' => [ 'modules' => [

View File

@ -101,7 +101,7 @@ class ConfigController extends Controller
try { try {
$form->setResourceConfig(ResourceFactory::getResourceConfigs()); $form->setResourceConfig(ResourceFactory::getResourceConfigs());
} catch (ConfigurationError $e) { } catch (ConfigurationError $e) {
if ($this->hasPermission('config/application/resources')) { if ($this->hasPermission('config/resources')) {
Notification::error($e->getMessage()); Notification::error($e->getMessage());
$this->redirectNow('config/createresource'); $this->redirectNow('config/createresource');
} }