<?php
/*
** Zabbix
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**/


require_once dirname(__FILE__).'/../../include/CWebTest.php';
require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
require_once dirname(__FILE__).'/../behaviors/CTableBehavior.php';
require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';

/**
 * @backup role, module, users, report, services
 *
 * @onBefore prepareUserData, prepareReportData, prepareServiceData
 */
class testUserRolesPermissions extends CWebTest {

	/**
	 * Attach MessageBehavior and TableBehavior to the test.
	 *
	 * @return array
	 */
	public function getBehaviors() {
		return [
			CMessageBehavior::class,
			CTableBehavior::class
		];
	}

	/**
	 * Id of role that created for future role change for Super admin.
	 *
	 * @var integer
	 */
	protected static $super_roleid;

	/**
	 * Id of user that created for future checks.
	 *
	 * @var integer
	 */
	protected static $super_user;

	/**
	 * Id of created scheduled report.
	 *
	 * @var integer
	 */
	protected static $reportid;

	/**
	 * Function used to create user.
	 */
	public function prepareUserData() {
		$role = CDataHelper::call('role.create', [
			[
				'name' => 'super_role',
				'type' => 3,
				'rules' => [
					'services.write.mode' => 1
				]
			]
		]);
		$this->assertArrayHasKey('roleids', $role);
		self::$super_roleid = $role['roleids'][0];

		$user = CDataHelper::call('user.create', [
			[
				'username' => 'user_for_role',
				'passwd' => 'zabbixzabbix',
				'roleid' => self::$super_roleid,
				'usrgrps' => [
					[
						'usrgrpid' => '7'
					]
				]
			]
		]);
		$this->assertArrayHasKey('userids', $user);
		self::$super_user = $user['userids'][0];
	}

	/**
	 * Scheduled report.
	 */
	public function prepareReportData() {
		$response = CDataHelper::call('report.create', [
			[
				'userid' => self::$super_user,
				'name' => 'test_report_for_role',
				'dashboardid' => '1',
				'users' => [
					[
						'userid' => self::$super_user,
						'exclude' => '0'
					]
				]
			]
		]);
		$this->assertArrayHasKey('reportids', $response);
		self::$reportid = $response['reportids'][0];
	}

	public function prepareServiceData() {
		// Remove all unnecessary services before proceeding with execution.
		DBExecute('DELETE FROM services');

		// Create services for Service permission checks.
		CDataHelper::call('service.create', [
			[
				'name' => 'Parent 1',
				'algorithm' => 1,
				'sortorder' => 1
			],
			[
				'name' => 'Parent 2',
				'algorithm' => 2,
				'sortorder' => 2
			],
			[
				'name' => 'Child of parent 1',
				'algorithm' => 2,
				'sortorder' => 1,
				'tags' => [
					[
						'tag' => 'test',
						'value' => 'test123'
					]
				]
			],
			[
				'name' => 'Child of child 1',
				'algorithm' => 2,
				'sortorder' => 1
			],
			[
				'name' => 'Child of parent 2',
				'algorithm' => 2,
				'sortorder' => 1
			]
		]);

		$services = CDataHelper::getIds('name');

		CDataHelper::call('service.update', [
			[
				'serviceid' =>  $services['Child of parent 1'],
				'parents' => [
					[
						'serviceid' => $services['Parent 1']
					]
				],
				'children' => [
					[
						'serviceid' => $services['Child of child 1']
					]
				]
			],
			[
				'serviceid' => $services['Child of parent 2'],
				'parents' => [
					[
						'serviceid' => $services['Parent 2']
					]
				]
			]
		]);
	}

	public static function getPageActionsData() {
		return [
			// Map creation/edit.
			[
				[
					'page_buttons' => [
						'Create map',
						'Import',
						'Delete'
					],
					'form_button' => [
						'Edit map'
					],
					'list_link' => 'sysmaps.php',
					'action_link' => 'zabbix.php?action=map.view&sysmapid=1',
					'action' => 'Create and edit maps',
					'check_links' => ['sysmap.php?sysmapid=1', 'sysmaps.php?form=Create+map']
				]
			],
			// Dashboard creation/edit.
			[
				[
					'page_buttons' => [
						'Create dashboard',
						'Delete'
					],
					'form_button' => [
						'Edit dashboard'
					],
					'list_link' => 'zabbix.php?action=dashboard.list',
					'action_link' => 'zabbix.php?action=dashboard.view&dashboardid=1220',
					'action' => 'Create and edit dashboards',
					'check_links' => ['zabbix.php?action=dashboard.view&new=1']
				]
			],
			// Maintenance creation/edit.
			[
				[
					'maintenance' => true,
					'page_buttons' => [
						'Create maintenance period',
						'Delete'
					],
					'form_button' => [
						'Update',
						'Clone',
						'Delete',
						'Cancel'
					],
					'list_link' => 'maintenance.php',
					'action_link' => 'maintenance.php?form=update&maintenanceid=5',
					'action' => 'Create and edit maintenance',
					'check_links' => ['maintenance.php?form=create']
				]
			],
			// Manage scheduled reports.
			[
				[
					'report' => true,
					'page_buttons' => [
						'Create report',
						'Enable',
						'Disable',
						'Delete'
					],
					'form_button' => [
						'Update',
						'Clone',
						'Test',
						'Delete',
						'Cancel'
					],
					'list_link' => 'zabbix.php?action=scheduledreport.list',
					'action' => 'Manage scheduled reports',
					'check_links' => ['zabbix.php?action=scheduledreport.edit']
				]
			]
		];
	}

	/**
	 * Check creation/edit for dashboard, map, reports, maintenance.
	 *
	 * @dataProvider getPageActionsData
	 */
	public function testUserRolesPermissions_PageActions($data) {
		$this->page->userLogin('user_for_role', 'zabbixzabbix');

		foreach ([true, false] as $action_status) {
			$this->page->open($data['list_link'])->waitUntilReady();

			$this->query('class:list-table')->asTable()->waitUntilVisible()->one()->getRow(0)->select();
			foreach ($data['page_buttons'] as $button) {
				$this->assertTrue($this->query('button', $button)->one()->isEnabled($action_status));
			}

			$this->page->open(array_key_exists('report', $data) ? 'zabbix.php?action=scheduledreport.edit&reportid='.
					self::$reportid : $data['action_link'])->waitUntilReady();

			foreach ($data['form_button'] as $text) {
				$this->assertTrue($this->query('button', $text)->one()->isEnabled(($text === 'Cancel') ? true : $action_status));
			}

			if ($action_status) {
				$this->changeRoleRule([$data['action'] => false]);
			}
		}

		$this->checkLinks($data['check_links']);
	}

	public static function getProblemActionsData() {
		return [
			// Message.
			[
				[
					'activityid' => 'message',
					'action' => 'Add problem comments',
					'column' => 'Message',
					'value' => 'test_text'
				]
			],
			// Severity.
			[
				[
					'activityid' => 'change_severity',
					'action' => 'Change severity',
					'column' => 'Severity',
					'value' => 'Average'
				]
			],
			// Close problem.
			[
				[
					'activityid' => 'close_problem',
					'action' => 'Close problems',
					'column' => 'Status',
					'value' => 'CLOSING'
				]
			],
			// Acknowledge problem.
			[
				[
					'activityid' => 'acknowledge_problem',
					'action' => 'Acknowledge problems',
					'column' => 'Ack',
					'value' => 'Yes'
				]
			]
		];
	}

	/**
	 * Check problem actions.
	 *
	 * @backupOnce events
	 *
	 * @dataProvider getProblemActionsData
	 */
	public function testUserRolesPermissions_ProblemAction($data) {
		$this->page->userLogin('user_for_role', 'zabbixzabbix');

		foreach ([true, false] as $action_status) {
			$this->page->open('zabbix.php?action=problem.view')->waitUntilReady();
			$row = $this->query('class:list-table')->asTable()->one()->findRow('Problem', 'Test trigger with tag');
			$row->getColumn('Ack')->query('link:No')->waitUntilClickable()->one()->click();
			$dialog = COverlayDialogElement::find()->waitUntilReady()->one();
			$this->assertTrue($dialog->query('id', $data['activityid'])->one()->isEnabled($action_status));
			$this->changeRoleRule([$data['action'] => !$action_status]);

			// Check that problem actions works after they were turned on.
			if ($action_status === false) {
				$this->page->open('zabbix.php?action=problem.view')->waitUntilReady();
				$row->getColumn('Ack')->query('link:No')->waitUntilCLickable()->one()->click();
				COverlayDialogElement::find()->waitUntilReady()->one();

				if ($data['activityid'] === 'message') {
					$dialog->query('id:message')->one()->fill('test_text');
					$dialog->query('button:Update')->one()->click();
					$this->page->waitUntilReady();
					$row->getColumn('Actions')->query('xpath:.//button[contains(@class, "icon-action-msgs")]')->one()->click();
					$message_hint = $this->query('xpath://div[@data-hintboxid]')->asOverlayDialog()->waitUntilPresent()->all()->last();
					$value = $message_hint->query('class:list-table')->asTable()->one()->getRow(0)->getColumn($data['column'])->getText();
					$this->assertEquals($data['value'], $value);
				}
				else {
					$dialog->query('id', $data['activityid'])->asCheckbox()->one()->check();

					if ($data['activityid'] === 'change_severity') {
						$dialog->query('id:severity')->asSegmentedRadio()->one()->fill('Average');
					}

					$dialog->query('button:Update')->one()->click();
					$this->page->waitUntilReady();
					$status = $row->getColumn($data['column'])->getText();
					$this->assertEquals($data['value'], $status);
				}
			}
		}
	}

	/**
	 * Check that Acknowledge link is disabled after all problem actions is disabled.
	 */
	public function testUserRolesPermissions_ProblemsActionsAll() {
		$problem = 'Test trigger with tag';
		$actions = [
			'Add problem comments' => false,
			'Change severity' => false,
			'Acknowledge problems' => false,
			'Close problems' => false
		];
		$this->page->userLogin('user_for_role', 'zabbixzabbix');

		foreach ([true, false] as $action_status) {
			// Problem page.
			$this->page->open('zabbix.php?action=problem.view')->waitUntilReady();
			$problem_row = $this->query('class:list-table')->asTable()->one()->findRow('Problem', $problem);
			$this->assertEquals($action_status, $problem_row->getColumn('Ack')->query('xpath:.//*[text()="No"]')
					->one()->isAttributePresent('onclick'));

			// Problem widget in dashboard.
			$this->page->open('zabbix.php?action=dashboard.view&dashboardid=1')->waitUntilReady();
			$table = CDashboardElement::find()->one()->getWidget('Problems')->query('class:list-table')->asTable()->one();
			$this->assertEquals($action_status, $table->findRow('Problem • Severity', $problem)->getColumn('Ack')
					->query('xpath:.//*[text()="No"]')->one()->isAttributePresent('onclick'));

			// Event details page.
			$this->page->open('tr_events.php?triggerid=99251&eventid=93')->waitUntilReady();

			foreach (['Event details', 'Event list [previous 20]'] as $table_name) {
				$table = $this->query('xpath://h4[text()='.CXPathHelper::escapeQuotes($table_name).']/../..//table')->asTable()->one();
				$this->assertEquals($action_status, $table->query('xpath:.//*[text()="No"]')
						->one()->isAttributePresent('onclick'));
			}

			if ($action_status) {
				$this->changeRoleRule($actions);
			}
		}
	}

	public static function getScriptActionData() {
		return [
			// Monitoring problems page.
			[
				[
					'link' => 'zabbix.php?action=problem.view',
					'selector' => 'xpath:(//a[@class="link-action wordbreak" and text()="ЗАББИКС Сервер"])[1]'
				]
			],
			// Dashboard problem widget.
			[
				[
					'link' => 'zabbix.php?action=dashboard.view&dashboardid=1',
					'selector' => 'link:ЗАББИКС Сервер'
				]
			],
			// Monitoring hosts page.
			[
				[
					'link' => 'zabbix.php?action=host.view',
					'selector' => 'link:3_Host_to_check_Monitoring_Overview'
				]
			],
			// Event detail page.
			[
				[
					'link' => 'tr_events.php?triggerid=99251&eventid=93',
					'selector' => 'xpath:(//*[@class="list-table"])[1]//*[text()="ЗАББИКС Сервер"]'
				]
			],
			// Monitoring maps page.
			[
				[
					'link' => 'zabbix.php?action=map.view&sysmapid=1',
					'selector' => 'xpath://*[name()="g"][@class="map-elements"]/*[name()="image"]'
				]
			]
		];
	}

	/**
	 * Check script actions.
	 *
	 * @dataProvider getScriptActionData
	 */
	public function testUserRolesPermissions_ScriptAction($data) {
		$context_before = [
			'Inventory',
			'Latest data',
			'Problems',
			'Graphs',
			'Dashboards',
			'Web',
			'Configuration',
			'Detect operating system',
			'Ping',
			'Traceroute'
		];
		$context_after = [
			'Inventory',
			'Latest data',
			'Problems',
			'Graphs',
			'Dashboards',
			'Web',
			'Configuration'
		];
		$this->page->userLogin('user_for_role', 'zabbixzabbix');

		foreach ([true, false] as $action_status) {
			$this->page->open($data['link'])->waitUntilReady();
			$this->query($data['selector'])->waitUntilPresent()->one()->click();

			$popup = CPopupMenuElement::find()->waitUntilVisible()->one();
			if ($action_status) {
				$this->assertTrue($popup->hasItems($context_before));
				$this->assertEquals(['HOST', 'SCRIPTS'], $popup->getTitles()->asText());
				$this->changeRoleRule(['Execute scripts' => false]);
			}
			else {
				$this->assertTrue($popup->hasItems($context_after));
				$this->assertEquals(['HOST'], $popup->getTitles()->asText());
				$this->changeRoleRule(['Execute scripts' => true]);
			}
		}
	}

	/**
	 * Module enable/disable.
	 */
	public function testUserRolesPermissions_Module() {
		$pages_before = [
			'Monitoring',
			'Services',
			'Inventory',
			'Reports',
			'Configuration',
			'Administration',
			'Module 5 menu'
		];
		$this->page->userLogin('user_for_role', 'zabbixzabbix');
		$this->page->open('zabbix.php?action=module.list')->waitUntilReady();
		$this->query('button:Scan directory')->one()->click();
		$this->query('class:list-table')->asTable()->one()->findRows('Name', '5th Module')->select();
		$this->query('button:Enable')->one()->click();
		$this->page->acceptAlert();
		$this->page->waitUntilReady();

		foreach ([true, false] as $action_status) {
			$page_number = $this->query('xpath://ul[@class="menu-main"]/li/a')->count();
			$all_pages = [];

			for ($i = 1; $i <= $page_number; ++$i) {
				$all_pages[] = $this->query('xpath:(//ul[@class="menu-main"]/li/a)['.$i.']')->one()->getText();
			}

			if ($action_status) {
				$this->assertEquals($pages_before, $all_pages);
				$this->changeRoleRule(['5th Module' => false]);
			}
			else {
				$pages_after = array_values(array_diff($pages_before, ['Module 5 menu']));
				$this->assertEquals($pages_after, $all_pages);
			}
		}
	}

	public static function getUIData() {
		return [
			[
				[
					'section' => 'Inventory',
					'page' => 'Overview',
					'displayed_ui' => [
						'Hosts'
					],
					'link' => ['hostinventoriesoverview.php']
				]
			],
			[
				[
					'section' => 'Inventory',
					'page' => 'Hosts',
					'displayed_ui' => [
							'Overview'
					],
					'link' => ['hostinventories.php']
				]
			],
			[
				[
					'section' => 'Reports',
					'page' => 'Availability report',
					'displayed_ui' => [
						'Scheduled reports',
						'System information',
						'Triggers top 100',
						'Audit',
						'Action log',
						'Notifications'
					],
					'link' => ['report2.php']
				]
			],
			[
				[
					'section' => 'Reports',
					'page' => 'System information',
					'displayed_ui' => [
						'Scheduled reports',
						'Availability report',
						'Triggers top 100',
						'Audit',
						'Action log',
						'Notifications'
					],
					'link' => ['zabbix.php?action=report.status']
				]
			],
			[
				[
					'section' => 'Reports',
					'page' => 'Availability report',
					'displayed_ui' => [
						'System information',
						'Scheduled reports',
						'Triggers top 100',
						'Audit',
						'Action log',
						'Notifications'
					],
					'link' => ['report2.php']
				]
			],
			[
				[
					'section' => 'Reports',
					'page' => 'Triggers top 100',
					'displayed_ui' => [
						'Availability report',
						'System information',
						'Scheduled reports',
						'Audit',
						'Action log',
						'Notifications'
					],
					'link' => ['toptriggers.php']
				]
			],
			[
				[
					'section' => 'Reports',
					'page' => 'Audit',
					'displayed_ui' => [
						'Availability report',
						'System information',
						'Scheduled reports',
						'Triggers top 100',
						'Action log',
						'Notifications'
					],
					'link' => ['zabbix.php?action=auditlog.list']
				]
			],
			[
				[
					'section' => 'Reports',
					'page' => 'Action log',
					'displayed_ui' => [
						'Availability report',
						'System information',
						'Scheduled reports',
						'Triggers top 100',
						'Audit',
						'Notifications'
					],
					'link' => ['auditacts.php']
				]
			],
			[
				[
					'section' => 'Reports',
					'page' => 'Notifications',
					'displayed_ui' => [
						'Availability report',
						'System information',
						'Scheduled reports',
						'Triggers top 100',
						'Audit',
						'Action log'
					],
					'link' => ['report4.php']
				]
			],
			[
				[
					'section' => 'Configuration',
					'page' => 'Host groups',
					'displayed_ui' => [
						'Templates',
						'Hosts',
						'Maintenance',
						'Actions',
						'Event correlation',
						'Discovery'
					],
					'link' => ['hostgroups.php']
				]
			],
			[
				[
					'section' => 'Configuration',
					'page' => 'Templates',
					'displayed_ui' => [
						'Host groups',
						'Hosts',
						'Maintenance',
						'Actions',
						'Event correlation',
						'Discovery'
					],
					'link' => ['templates.php']
				]
			],
			[
				[
					'section' => 'Configuration',
					'page' => 'Hosts',
					'displayed_ui' => [
						'Host groups',
						'Templates',
						'Maintenance',
						'Actions',
						'Event correlation',
						'Discovery'
					],
					'link' => ['zabbix.php?action=host.list']
				]
			],
			[
				[
					'section' => 'Configuration',
					'page' => 'Maintenance',
					'displayed_ui' => [
						'Host groups',
						'Templates',
						'Hosts',
						'Actions',
						'Event correlation',
						'Discovery'
					],
					'link' => ['maintenance.php']
				]
			],
			[
				[
					'section' => 'Configuration',
					'page' => 'Actions',
					'displayed_ui' => [
						'Host groups',
						'Templates',
						'Hosts',
						'Maintenance',
						'Event correlation',
						'Discovery'
					],
					'link' => [
						'actionconf.php?eventsource=0',
						'actionconf.php?eventsource=1',
						'actionconf.php?eventsource=2',
						'actionconf.php?eventsource=3'
					]
				]
			],
			[
				[
					'section' => 'Configuration',
					'page' => 'Event correlation',
					'displayed_ui' => [
						'Host groups',
						'Templates',
						'Hosts',
						'Maintenance',
						'Actions',
						'Discovery'
					],
					'link' => ['zabbix.php?action=correlation.list']
				]
			],
			[
				[
					'section' => 'Configuration',
					'page' => 'Discovery',
					'displayed_ui' => [
						'Host groups',
						'Templates',
						'Hosts',
						'Maintenance',
						'Actions',
						'Event correlation'
					],
					'link' => ['zabbix.php?action=discovery.list']
				]
			],
			[
				[
					'section' => 'Administration',
					'page' => 'General',
					'displayed_ui' => [
						'Proxies',
						'Authentication',
						'User groups',
						'User roles',
						'Users',
						'Media types',
						'Scripts',
						'Queue'
					],
					'link' => [
						'zabbix.php?action=gui.edit',
						'zabbix.php?action=autoreg.edit',
						'zabbix.php?action=housekeeping.edit',
						'zabbix.php?action=image.list',
						'zabbix.php?action=iconmap.list',
						'zabbix.php?action=regex.list',
						'zabbix.php?action=macros.edit',
						'zabbix.php?action=token.list',
						'zabbix.php?action=trigdisplay.edit',
						'zabbix.php?action=module.list',
						'zabbix.php?action=miscconfig.edit'
					]
				]
			],
			[
				[
					'section' => 'Administration',
					'page' => 'Proxies',
					'displayed_ui' => [
						'General',
						'Authentication',
						'User groups',
						'User roles',
						'Users',
						'Media types',
						'Scripts',
						'Queue'
					],
					'link' => ['zabbix.php?action=proxy.list']
				]
			],
			[
				[
					'section' => 'Administration',
					'page' => 'Authentication',
					'displayed_ui' => [
						'General',
						'Proxies',
						'User groups',
						'User roles',
						'Users',
						'Media types',
						'Scripts',
						'Queue'
					],
					'link' => ['zabbix.php?action=authentication.edit']
				]
			],
			[
				[
					'section' => 'Administration',
					'page' => 'User groups',
					'displayed_ui' => [
						'General',
						'Proxies',
						'Authentication',
						'User roles',
						'Users',
						'Media types',
						'Scripts',
						'Queue'
					],
					'link' => ['zabbix.php?action=usergroup.list']
				]
			],
			[
				[
					'section' => 'Administration',
					'page' => 'Users',
					'displayed_ui' => [
						'General',
						'Proxies',
						'Authentication',
						'User roles',
						'User groups',
						'Media types',
						'Scripts',
						'Queue'
					],
					'link' => ['zabbix.php?action=user.list']
				]
			],
			[
				[
					'section' => 'Administration',
					'page' => 'Media types',
					'displayed_ui' => [
						'General',
						'Proxies',
						'Authentication',
						'User roles',
						'User groups',
						'Users',
						'Scripts',
						'Queue'
					],
					'link' => ['zabbix.php?action=mediatype.list']
				]
			],
			[
				[
					'section' => 'Administration',
					'page' => 'Scripts',
					'displayed_ui' => [
						'General',
						'Proxies',
						'Authentication',
						'User roles',
						'User groups',
						'Users',
						'Media types',
						'Queue'
					],
					'link' => ['zabbix.php?action=script.list']
				]
			],
			[
				[
					'section' => 'Administration',
					'page' => 'Queue',
					'displayed_ui' => [
						'General',
						'Proxies',
						'Authentication',
						'User roles',
						'User groups',
						'Users',
						'Media types',
						'Scripts'
					],
					'link' => [
						'zabbix.php?action=queue.overview',
						'zabbix.php?action=queue.overview.proxy',
						'zabbix.php?action=queue.details'
					]
				]
			],
			[
				[
					'section' => 'Administration',
					'user_roles' => true,
					'page' => 'User roles',
					'displayed_ui' => [
						'General',
						'Proxies',
						'Authentication',
						'Queue',
						'User groups',
						'Users',
						'Media types',
						'Scripts'
					],
					'link' => ['zabbix.php?action=userrole.list']
				]
			],
			[
				[
					'section' => 'Monitoring',
					'page' => 'Problems',
					'displayed_ui' => [
						'Dashboard',
						'Hosts',
						'Latest data',
						'Maps',
						'Discovery'
					],
					'link' => ['zabbix.php?action=problem.view']
				]
			],
			[
				[
					'section' => 'Monitoring',
					'page' => 'Hosts',
					'displayed_ui' => [
						'Dashboard',
						'Problems',
						'Latest data',
						'Maps',
						'Discovery'
					],
					'link' => ['zabbix.php?action=host.view']
				]
			],
			[
				[
					'section' => 'Monitoring',
					'page' => 'Latest data',
					'displayed_ui' => [
						'Dashboard',
						'Problems',
						'Hosts',
						'Maps',
						'Discovery'
					],
					'link' => ['zabbix.php?action=latest.view']
				]
			],
			[
				[
					'section' => 'Monitoring',
					'page' => 'Maps',
					'displayed_ui' => [
						'Dashboard',
						'Problems',
						'Hosts',
						'Latest data',
						'Discovery'
					],
					'link' => ['sysmaps.php']
				]
			],
			[
				[
					'section' => 'Monitoring',
					'page' => 'Discovery',
					'displayed_ui' => [
						'Dashboard',
						'Problems',
						'Hosts',
						'Latest data',
						'Maps'
					],
					'link' => ['zabbix.php?action=discovery.view']
				]
			],
			[
				[
					'section' => 'Services',
					'page' => 'Services',
					'displayed_ui' => [
						'Service actions',
						'SLA',
						'SLA report'
					],
					'link' => ['zabbix.php?action=service.list']
				]
			],
			[
				[
					'section' => 'Services',
					'page' => 'Service actions',
					'displayed_ui' => [
						'Services',
						'SLA',
						'SLA report'
					],
					'link' => ['actionconf.php?eventsource=4']
				]
			],
			[
				[
					'section' => 'Services',
					'page' => 'SLA',
					'displayed_ui' => [
						'Services',
						'Service actions',
						'SLA report'
					],
					'link' => ['zabbix.php?action=sla.list']
				]
			],
			[
				[
					'section' => 'Services',
					'page' => 'SLA report',
					'displayed_ui' => [
						'Services',
						'Service actions',
						'SLA'
					],
					'link' => ['zabbix.php?action=slareport.list']
				]
			]
		];
	}

	/**
	 * UI permission
	 *
	 * @dataProvider getUIData
	 */
	public function testUserRolesPermissions_UI($data) {
		$user_roles = [
			'Administration' => [
				'General',
				'Proxies',
				'Authentication',
				'User roles',
				'User groups',
				'Users',
				'Media types',
				'Scripts'
			]
		];
		$this->page->userLogin('user_for_role', 'zabbixzabbix');

		foreach ([true, false] as $action_status) {
			$menu = CMainMenuElement::find()->one();
			if ($data['section'] !== 'Monitoring') {
				$menu->select($data['section']);
			}

			if ($data['page'] === $data['section']) {
				$submenu = $menu->query("xpath:.//a[text()=".CXPathHelper::escapeQuotes($data['section']).
						"]/../ul[@class='submenu']")->one();
				$this->assertEquals($action_status, $submenu->query('link', $data['page'])->one(false)->isValid());
			}
			else {
				$this->assertEquals($action_status, $menu->exists($data['page']));
			}

			if ($action_status) {
				if (array_key_exists('user_roles', $data)) {
					$this->signOut();
					$this->page->userLogin('Admin', 'zabbix');
					$this->changeRoleRule([$data['section'] => $data['displayed_ui']]);
					$this->signOut();
					$this->page->userLogin('user_for_role', 'zabbixzabbix');
				}
				else {
					$this->changeRoleRule([$data['section'] => $data['displayed_ui']]);
					$this->page->open('zabbix.php?action=dashboard.view')->waitUntilReady();
				}
			}
			else {
				if (array_key_exists('user_roles', $data)) {
					$this->checkLinks($data['link']);
					$this->signOut();
					$this->page->userLogin('Admin', 'zabbix');
					$this->changeRoleRule($user_roles);
					$this->signOut();
				}
				else {
					$this->checkLinks($data['link']);
					$this->signOut();
				}
			}
		}
	}

	public static function getDashboardData() {
		return [
			[
				[
					'page' => 'Dashboard',
					'button' => 'Problems',
					'displayed_ui' => [
						'Problems',
						'Hosts',
						'Latest data',
						'Maps',
						'Discovery'
					]
				]
			],
			[
				[
					'button' => 'Hosts',
					'displayed_ui' => [
						'Hosts',
						'Latest data',
						'Maps',
						'Discovery'
					]
				]
			]
//			TODO: uncomment after ZBX-19479 fix.
//			[
//				[
//					'button' => 'Overview',
//					'displayed_ui' => [
//						'Overview',
//						'Latest data',
//						'Maps',
//						'Discovery'
//					]
//				]
//			]
		];
	}

	/**
	 * Disabling access to Dashboard. Check warning message text and button.
	 *
	 * @dataProvider getDashboardData
	 */
	public function testUserRolesPermissions_Dashboard($data) {
		$this->page->userLogin('user_for_role', 'zabbixzabbix');

		foreach ([true, false] as $action_status) {
			$main_section = $this->query('xpath://ul[@class="menu-main"]')->query('link:Monitoring');

			if (array_key_exists('page', $data)) {
				$this->assertEquals($action_status, $main_section->one()->parents('tag:li')->query('link', $data['page'])->exists());
			}

			if ($action_status) {
				$this->changeRoleRule(['Monitoring' => $data['displayed_ui']]);
			}
			else {
				$this->checkLinks(['zabbix.php?action=dashboard.view'], $data['button']);
				$this->changeRoleRule(['Monitoring' => ['Dashboard', 'Problems', 'Hosts', 'Latest data', 'Maps',
						'Discovery']]
				);
			}
		}
	}

	/**
	 * Manage API token action check.
	 */
	public function testUserRolesPermissions_ManageApiToken() {
		$this->page->userLogin('user_for_role', 'zabbixzabbix');
		$this->page->open('zabbix.php?action=user.token.list')->waitUntilReady();
		$this->assertEquals('TEST_SERVER_NAME: API tokens', $this->page->getTitle());
		$this->changeRoleRule(['Manage API tokens' => false]);
		$this->checkLinks(['zabbix.php?action=user.token.list']);
		$this->page->logout();
	}

	public static function getRoleServiceData() {
		return [
			[
				[
					'role_config' => [
						'Read-write access to services' => 'None',
						'Read-only access to services' => 'None'
					],
					'services' => null
				]
			],
			[
				[
					'role_config' => [
						'Read-write access to services' => 'All',
						'Read-only access to services' => 'None'
					],
					'services' => [
						'Child of child 1' => 'write',
						'Child of parent 1' => 'write',
						'Child of parent 2' => 'write',
						'Parent 1' => 'write',
						'Parent 2' => 'write'
					]
				]
			],
			[
				[
					'role_config' => [
						'Read-write access to services' => 'None',
						'Read-only access to services' => 'All'
					],
					'services' => [
						'Child of child 1' => 'read',
						'Child of parent 1' => 'read',
						'Child of parent 2' => 'read',
						'Parent 1' => 'read',
						'Parent 2' => 'read'
					]
				]
			],
			[
				[
					'role_config' => [
						'Read-write access to services' => 'All',
						'Read-only access to services' => 'All'
					],
					'services' => [
						'Child of child 1' => 'write',
						'Child of parent 1' => 'write',
						'Child of parent 2' => 'write',
						'Parent 1' => 'write',
						'Parent 2' => 'write'
					]
				]
			],
			[
				[
					'role_config' => [
						'Read-write access to services' => 'Service list',
						'Read-only access to services' => 'None'
					],
					'service_list' => [
						'Read-write access to services with tag' => [
							'service-write-tag-tag' => 'test',
							'service_write_tag_value' => 'test123'
						]
					],
					'services' => [
						'Child of child 1' => 'write',
						'Child of parent 1' => 'write'
					]
				]
			],
			[
				[
					'role_config' => [
						'Read-write access to services' => 'None',
						'Read-only access to services' => 'Service list'
					],
					'service_list' => [
						'Read-only access to services with tag' => [
							'service-read-tag-tag' => 'test',
							'service_read_tag_value' => 'test123'
						]
					],
					'services' => [
						'Child of child 1' => 'read',
						'Child of parent 1' => 'read'
					]
				]
			],
			[
				[
					'role_config' => [
						'Read-write access to services' => 'Service list',
						'Read-only access to services' => 'None'
					],
					'service_list' => [
						'xpath:(//div[@class="multiselect-control"])[1]' => 'Child of parent 1'
					],
					'services' => [
						'Child of child 1' => 'write',
						'Child of parent 1' => 'write'
					]
				]
			],
			[
				[
					'role_config' => [
						'Read-write access to services' => 'None',
						'Read-only access to services' => 'Service list'
					],
					'service_list' => [
						'xpath:(//div[@class="multiselect-control"])[2]' => 'Child of parent 1'
					],
					'services' => [
						'Child of child 1' => 'read',
						'Child of parent 1' => 'read'
					]
				]
			],
			[
				[
					'role_config' => [
						'Read-write access to services' => 'Service list',
						'Read-only access to services' => 'All'
					],
					'service_list' => [
						'xpath:(//div[@class="multiselect-control"])[1]' => 'Child of parent 1'
					],
					'services' => [
						'Child of child 1' => 'write',
						'Child of parent 1' => 'write',
						'Child of parent 2' => 'read',
						'Parent 1' => 'read',
						'Parent 2' => 'read'
					]
				]
			]
		];
	}

	/**
	 * Check permissions to services based on user role configuration.
	 *
	 * @dataProvider getRoleServiceData
	 */
	public function testUserRolesPermissions_ServicePermissions($data) {
		// Prepare a combination of service name and the number of child services for service for further comparison.
		if ($data['services'] !== null) {
			$child_services = [
				'Child of parent 1' => 1,
				'Parent 1' => 1,
				'Parent 2' => 1
			];
			$column_content = [];

			foreach (array_keys($data['services']) as $service) {
				$column_content[] = array_key_exists($service, $child_services)
					? $service.' '.$child_services[$service]
					: $service;
			}
		}

		// Configure the role according to the data provider.
		$this->page->login()->open('zabbix.php?action=userrole.edit&roleid='.self::$super_roleid)->waitUntilReady();
		$form = $this->query('id:userrole-form')->waitUntilPresent()->asForm()->one();
		$form->fill($data['role_config']);

		if (array_key_exists('service_list', $data)) {
			$form->fill($data['service_list']);
		}
		$form->submit();
		$this->assertMessage(TEST_GOOD, 'User role updated');
		$this->page->logout();

		// Login as user that belongs to the updated row and check access to services based on applied configuration.
		$this->page->userLogin('user_for_role', 'zabbixzabbix');
		$this->page->open('zabbix.php?action=service.list')->waitUntilReady();
		$this->assertEquals('user_for_role', $this->query('xpath://a[text()="User settings"]')->one()->getAttribute('title'));

		$services_mode = $this->query('id:list_mode')->asSegmentedRadio()->one(false);

		// Check that table service list content and edit mode in not available if the user doest have permissions.
		if ($data['services'] === null) {
			$this->assertTableData();
			$this->assertFalse($services_mode->isValid());

			return;
		}
		elseif ($data['role_config']['Read-write access to services'] !== 'None') {
			// Open edit mode if user has write permissions to at least one service.
			$services_mode->select('Edit');
			$this->page->waituntilReady();
		}

		// Filter out unnecessary services.
		$this->query('id:filter_tags_0_tag')->waitUntilVisible()->one()->fill('action');
		$this->query('id:filter_tags_0_operator')->asDropdown()->waitUntilVisible()->one()->fill('Does not exist');

		// Apply filter in order to see the list of available services.
		$this->query('name:filter_set')->waitUntilClickable()->one()->click();
		$this->page->waituntilReady();

		$this->assertTableDataColumn($column_content, 'Name');
		$table = $this->query('class:list-table')->asTable()->one();

		// Check buttons are not visible for user with no permissions, otherwise, check edit permissions per service.
		if ($data['role_config']['Read-write access to services'] === 'None') {
			foreach ($table->getRows() as $row) {
				$this->assertEquals(0, $row->query('xpath:.//button')->all(false)->count());
			}
		}
		else {
			foreach ($data['services'] as $service => $permissions) {
				$property = ($permissions === 'write') ? CElementFilter::CLICKABLE : CElementFilter::NOT_CLICKABLE;
				$row = $table->findRow('Name', $service, true);
				// Check that all three action buttons in the row are clickable.
				$this->assertEquals(3, $row->query("xpath:.//button")->all()
						->filter(new CElementFilter($property))->count()
				);
			}
		}
	}

	/**
	 * Check disabled actions with links.
	 *
	 * @param array $links		checked links after disabling action
	 * @param string $page		page name displayed on error message button
	 */
	private function checkLinks($links, $page = 'Dashboard') {
		foreach ($links as $link) {
			$this->page->open($link)->waitUntilReady();
			$this->assertMessage(TEST_BAD, 'Access denied', 'You are logged in as "user_for_role". '.
					'You have no permissions to access this page.');
			$this->query('button:Go to "'.$page.'"')->one()->waitUntilClickable()->click();

			if ($page === 'Dashboard') {
				$this->assertStringContainsString('zabbix.php?action=dashboard', $this->page->getCurrentUrl());
			}
		}
	}

	/**
	 * Enable/disable actions and UI.
	 *
	 * @param array $action		action with true/false status or UI section with page
	 */
	private function changeRoleRule($action) {
		$this->page->open('zabbix.php?action=userrole.edit&roleid='.self::$super_roleid)->waitUntilReady();
		$this->query('id:userrole-form')->waitUntilPresent()->asForm()->one()->fill($action)->submit();
		$this->page->waitUntilReady();
		$this->assertMessage(TEST_GOOD, 'User role updated');
	}

	/**
	 * Click Sign out button.
	 */
	private function signOut() {
		$this->query('xpath://a[@class="icon-signout"]')->waitUntilPresent()->one()->click();
		$this->page->waitUntilReady();
		$this->query('button:Sign in')->waitUntilVisible();
	}
}