diff options
author | Simon Rettberg | 2018-02-27 13:05:21 +0100 |
---|---|---|
committer | Simon Rettberg | 2018-02-27 13:05:21 +0100 |
commit | 47d1e28478a78eb8d275553cb44693a61b597307 (patch) | |
tree | 447bbbd333f19db716e6f0a1b773f21d01d72301 /modules-available/runmode | |
parent | [dnbd3] Add permissions (diff) | |
download | slx-admin-47d1e28478a78eb8d275553cb44693a61b597307.tar.gz slx-admin-47d1e28478a78eb8d275553cb44693a61b597307.tar.xz slx-admin-47d1e28478a78eb8d275553cb44693a61b597307.zip |
[runmode] Support permissions supplied by module; fix handling of deleteUrl modules
Diffstat (limited to 'modules-available/runmode')
5 files changed, 242 insertions, 94 deletions
diff --git a/modules-available/runmode/inc/runmode.inc.php b/modules-available/runmode/inc/runmode.inc.php index 2c8083ca..174fb675 100644 --- a/modules-available/runmode/inc/runmode.inc.php +++ b/modules-available/runmode/inc/runmode.inc.php @@ -28,7 +28,7 @@ class RunMode /** * @param string $machineuuid - * @param string $moduleId + * @param string|\Module $moduleId * @param string|null $modeId an ID specific to the module to further specify the run mode, NULL to delete the run mode entry * @param string|null $modeData optional, additional data for the run mode * @param bool|null $isClient whether to count the machine as a client (in statistics etc.) NULL for looking at module's general runmode config @@ -284,6 +284,11 @@ class RunModeModuleConfig */ public $deleteUrlSnippet = false; + /** + * @var string|false Permission to check when accessing/assigning + */ + public $permission = false; + public function __construct($file) { $data = json_decode(file_get_contents($file), true); @@ -298,6 +303,7 @@ class RunModeModuleConfig $this->loadType($data, 'noSysconfig', 'boolean'); $this->loadType($data, 'allowGenericEditor', 'boolean'); $this->loadType($data, 'deleteUrlSnippet', 'string'); + $this->loadType($data, 'permission', 'string'); } private function loadType($data, $key, $type) @@ -311,4 +317,10 @@ class RunModeModuleConfig $this->{$key} = $data[$key]; return true; } + + public function userHasPermission($locationId) + { + return $this->permission === false || User::hasPermission($this->permission, $locationId); + } + } diff --git a/modules-available/runmode/page.inc.php b/modules-available/runmode/page.inc.php index 8feb097e..9f2c2f44 100644 --- a/modules-available/runmode/page.inc.php +++ b/modules-available/runmode/page.inc.php @@ -14,79 +14,142 @@ class Page_RunMode extends Page Util::redirect('?do=main'); } $action = Request::post('action', false, 'string'); - if ($action !== false) { - $this->handleAction($action); + if ($action === 'save-mode') { + $this->handleSaveMode(); + } elseif ($action === 'delete-machine') { + $this->handleDeleteMachine(); + } + if (Request::isPost()) { Util::redirect('?do=runmode'); } } - private function handleAction($action) + private function handleSaveMode() { - if ($action === 'save-mode') { - $machines = array_filter(Request::post('machines', [], 'array'), 'is_string'); - $module = Request::post('module', false, 'string'); - $modeId = Request::post('modeid', false, 'string'); - $modConfig = RunMode::getModuleConfig($module); - if ($modConfig === false) { - Message::addError('runmode.module-hasnt-runmode', $module); - return; - } - if (!$modConfig->allowGenericEditor) { - Message::addError('runmode.cannot-edit-module', $module); - return; - } - $test = RunMode::getModeName($module, $modeId); - if ($test === false) { - Message::addError('runmode.invalid-modeid', $module, $modeId); - return; - } - $active = 0; - foreach ($machines as $machine) { - $oldMode = RunMode::getRunMode($machine, 0); - if ($oldMode !== false) { - $oldModule = RunMode::getModuleConfig($oldMode['module']); - if ($oldModule !== false && (!$oldModule->allowGenericEditor || $oldModule->deleteUrlSnippet !== false)) { - Message::addError('runmode.machine-still-assigned', $machine, $oldMode['module']); - continue; - } + $machines = array_unique(array_filter(Request::post('machines', [], 'array'), 'is_string')); + $module = Request::post('module', false, 'string'); + $modeId = Request::post('modeid', false, 'string'); + $modConfig = RunMode::getModuleConfig($module); + if ($modConfig === false) { + Message::addError('runmode.module-hasnt-runmode', $module); + return; + } + if (!$modConfig->allowGenericEditor) { + Message::addError('runmode.cannot-edit-module', $module); + return; + } + $test = RunMode::getModeName($module, $modeId); + if ($test === false) { + Message::addError('runmode.invalid-modeid', $module, $modeId); + return; + } + // Query existing entries first (for delete - see below) + $existing = []; + if ($modConfig->permission !== false) { + $existing = RunMode::getForMode($module, $modeId, true, true); + } + // Before doing anything, check if the user has the proper permission for any location - if not, nothing to do + if (!$modConfig->userHasPermission(null)) { + Message::addError('main.no-permission'); + Util::redirect('?do=runmode'); + } + $active = 0; + foreach ($machines as $machine) { + if (isset($existing[$machine])) { + if (!$modConfig->userHasPermission($existing[$machine]['locationid'])) { + // Machine was already assigned to this runmode, and user has no permission for its location + unset($existing[$machine]); + continue; // So keep it as-is and skip } - $ret = RunMode::setRunMode($machine, $module, $modeId, null, null); - if ($ret) { - $active++; + // User has permission to add this existing machine, keep going so meta data could be updated + unset($existing[$machine]); + } else { + // Not existing yet in this module/mode combo, but check if it is assigned to some other run mode + $machineLocation = false; + $oldMachineMode = RunMode::getRunMode($machine, RunMode::DATA_MACHINE_DATA | RunMode::DATA_DETAILED); + if ($oldMachineMode !== false) { + $machineLocation = $oldMachineMode['locationid']; + $oldModule = RunMode::getModuleConfig($oldMachineMode['module']); + if ($oldModule !== false) { + if ($oldMachineMode['module'] !== $module || $oldMachineMode['modeid'] !== $modeId) { + if (!$oldModule->allowGenericEditor || $oldModule->deleteUrlSnippet !== false) { + Message::addError('runmode.machine-still-assigned', $machine, $oldMachineMode['module']); + continue; + } + } + // Permissions for old runmode + if (!$oldModule->userHasPermission($oldMachineMode['locationid'])) { + // Show same error message as above - might help the user figure out they have no perm to remove it + Message::addError('runmode.machine-still-assigned', $machine, $oldMachineMode['module']); + continue; + } + } } else { - Message::addError('runmode.machine-not-found', $machine); + // Not existing, no old mode - query machine to get location, so we can do a perm-check for new loc + $m = Statistics::getMachine($machine, Machine::NO_DATA); + if ($m !== false) { + $machineLocation = $m->locationid; + } + } + if ($machineLocation !== false && !$modConfig->userHasPermission($machineLocation)) { + Message::addError('runmode.machine-no-permission', $machine); + continue; } } + $ret = RunMode::setRunMode($machine, $module, $modeId, null, null); + if ($ret) { + $active++; + } else { + Message::addError('runmode.machine-not-found', $machine); + } + } + // Make sure inaccessible machines (no permission for location) are preserved on delete + // Add existing but inaccessible to list + foreach ($existing as $e) { + if (!$modConfig->userHasPermission($e['locationid'])) { + $machines[] = $e['machineuuid']; + } + } + if ($modConfig->deleteUrlSnippet === false) { $deleted = Database::exec('DELETE FROM runmode WHERE module = :module AND modeid = :modeId AND machineuuid NOT IN (:machines)', compact('module', 'modeId', 'machines')); - Message::addSuccess('runmode.enabled-removed-save', $active, $deleted); - Util::redirect('?do=runmode&module=' . $module . '&modeid=' . $modeId, true); - } elseif ($action === 'delete-machine') { - $machineuuid = Request::post('machineuuid', false, 'string'); - if ($machineuuid === false) { - Message::addError('main.parameter-missing', 'machineuuid'); - return; - } - $mode = RunMode::getRunMode($machineuuid); - if ($mode === false) { - Message::addError('runmode.machine-not-found', $machineuuid); - return; - } - $modConfig = RunMode::getModuleConfig($mode['module']); - if ($modConfig === false) { - Message::addError('module-hasnt-runmode', $mode['moduleName']); - return; - } - if (!$modConfig->allowGenericEditor) { - Message::addError('runmode.cannot-edit-module', $mode['moduleName']); - return; - } - if (RunMode::setRunMode($machineuuid, null, null)) { - Message::addSuccess('machine-removed', $machineuuid); - } else { - Message::addWarning('machine-not-runmode', $machineuuid); - } + } else { + $deleted = 0; + } + Message::addSuccess('runmode.enabled-removed-save', $active, $deleted); + Util::redirect('?do=runmode&module=' . $module . '&modeid=' . $modeId, true); + } + + private function handleDeleteMachine() + { + $machineuuid = Request::post('machineuuid', false, 'string'); + if ($machineuuid === false) { + Message::addError('main.parameter-missing', 'machineuuid'); + return; + } + $mode = RunMode::getRunMode($machineuuid, RunMode::DATA_MACHINE_DATA | RunMode::DATA_DETAILED); + if ($mode === false) { + Message::addError('runmode.machine-not-found', $machineuuid); + return; + } + $modConfig = RunMode::getModuleConfig($mode['module']); + if ($modConfig === false) { + Message::addError('module-hasnt-runmode', $mode['moduleName']); + return; + } + if (!$modConfig->allowGenericEditor || $modConfig->deleteUrlSnippet !== false) { + Message::addError('runmode.cannot-edit-module', $mode['moduleName']); + return; + } + if (!$modConfig->userHasPermission($mode['locationid'])) { + Message::addError('runmode.machine-no-permission', $machineuuid); + return; + } + if (RunMode::setRunMode($machineuuid, null, null)) { + Message::addSuccess('machine-removed', $machineuuid); + } else { + Message::addWarning('machine-not-runmode', $machineuuid); } } @@ -117,7 +180,13 @@ class Page_RunMode extends Page $modeId = Request::get('modeid', false, 'string'); if ($modeId !== false) { // Show edit page for specific module-mode combo - $this->renderModuleMode($module, $modeId); + $this->renderModuleMode($module, $modeId, $config); + return; + } + // Permissions + if (!$config->userHasPermission(null) && !User::hasPermission('list-all')) { + Message::addError('main.no-permission'); + Util::redirect('?do=runmode'); return; } // Show list of machines with assigned mode for this module @@ -152,6 +221,16 @@ class Page_RunMode extends Page $modules[$row['module']]['list'][] = $row; } foreach ($modules as $moduleId => $rows) { + if ($onlyModule === false) { + // Permissions - not required if rendering specific module, since it's been already done + if ($rows['config']->userHasPermission(null)) { + $disabled = ''; + } elseif (User::hasPermission('list-all')) { + $disabled = 'disabled'; + } else { + continue; + } // </Permissions> + } $module = Module::get($moduleId); if ($module === false) continue; @@ -161,7 +240,8 @@ class Page_RunMode extends Page 'modulename' => $module->getDisplayName(), 'module' => $moduleId, 'canedit' => $config !== false && $config->allowGenericEditor && $config->deleteUrlSnippet === false, - 'deleteUrl' => $config->deleteUrlSnippet + 'deleteUrl' => $config->deleteUrlSnippet, + 'disabled' => $disabled, )); } } @@ -169,8 +249,9 @@ class Page_RunMode extends Page /** * @param \Module $module * @param string $modeId + * @param \RunModeModuleConfig $config */ - private function renderModuleMode($module, $modeId) + private function renderModuleMode($module, $modeId, $config) { $moduleId = $module->getIdentifier(); $modeName = RunMode::getModeName($moduleId, $modeId); @@ -186,39 +267,78 @@ class Page_RunMode extends Page Message::addError('runmode.cannot-edit-module', $moduleId); Util::redirect($redirect); } + // Permissions + if ($config->userHasPermission(null)) { + $disabled = ''; + } elseif (User::hasPermission('list-all')) { + $disabled = 'disabled'; + } else { + Message::addError('main.no-permission'); + Util::redirect('?do=runmode'); + return; + } + $machines = RunMode::getForMode($module, $modeId, true); + if ($config->permission !== false) { + $allowed = User::getAllowedLocations($config->permission); + $machines = array_values(array_filter($machines, function ($item) use ($allowed) { + return in_array($item['locationid'], $allowed); + })); + } Render::addTemplate('machine-selector', [ 'module' => $moduleId, 'modeid' => $modeId, 'moduleName' => $module->getDisplayName(), 'modeName' => $modeName, - 'machines' => json_encode(RunMode::getForMode($module, $modeId, true)), + 'machines' => json_encode($machines), 'redirect' => $redirect, + 'disabled' => $disabled, + 'add-only' => $config->deleteUrlSnippet !== false, ]); } protected function doAjax() { $action = Request::any('action', false, 'string'); + if ($action !== 'getmachines') + return; + $query = Request::get('query', false, 'string'); + if (strlen($query) < 2) + return; - if ($action === 'getmachines') { - $query = Request::get('query', false, 'string'); - - $result = Database::simpleQuery('SELECT m.machineuuid, m.macaddr, m.clientip, m.hostname, m.locationid, ' - . 'r.module, r.modeid ' - . 'FROM machine m ' - . 'LEFT JOIN runmode r USING (machineuuid) ' - . 'WHERE machineuuid LIKE :query ' - . ' OR macaddr LIKE :query ' - . ' OR clientip LIKE :query ' - . ' OR hostname LIKE :query ' - . ' LIMIT 100', ['query' => "%$query%"]); + User::load(); + $config = RunMode::getModuleConfig(Request::any('module', '', 'string')); + $returnObject = ['machines' => []]; - $returnObject = [ - 'machines' => $result->fetchAll(PDO::FETCH_ASSOC) - ]; + if ($config !== false) { + $params = ['query' => "%$query%"]; + if ($config->permission === false) { + // Global + $condition = '1'; + } else { + $params['locations'] = User::getAllowedLocations($config->permission); + $condition = 'locationid IN (:locations)'; + if (in_array(0, $params['locations'])) { + $condition .= ' OR locationid IS NULL'; + } + } + if ($config->permission === false || !empty($params['locations'])) { + $result = Database::simpleQuery("SELECT m.machineuuid, m.macaddr, m.clientip, m.hostname, m.locationid, + r.module, r.modeid + FROM machine m + LEFT JOIN runmode r USING (machineuuid) + WHERE ($condition) AND (machineuuid LIKE :query + OR macaddr LIKE :query + OR clientip LIKE :query + OR hostname LIKE :query) + LIMIT 100", $params); - echo json_encode($returnObject); + $returnObject = [ + 'machines' => $result->fetchAll(PDO::FETCH_ASSOC) + ]; + } } + echo json_encode($returnObject); + } }
\ No newline at end of file diff --git a/modules-available/runmode/permissions/permissions.json b/modules-available/runmode/permissions/permissions.json new file mode 100644 index 00000000..53dfdab0 --- /dev/null +++ b/modules-available/runmode/permissions/permissions.json @@ -0,0 +1,5 @@ +{ + "list-all": { + "location-aware": false + } +}
\ No newline at end of file diff --git a/modules-available/runmode/templates/machine-selector.html b/modules-available/runmode/templates/machine-selector.html index 7f37f5a2..8b608f7e 100644 --- a/modules-available/runmode/templates/machine-selector.html +++ b/modules-available/runmode/templates/machine-selector.html @@ -1,12 +1,7 @@ <h1>{{lang_assignRunmodeToMachine}}</h1> <h2>{{moduleName}} // {{modeName}}</h2> -<p>{{lang_assignMachineIntroText}}</p> -<div class="hidden"> - {{#machines}} - <div id="qex-{{machineuuid}}">{{hostname}}</div> - {{/machines}} -</div> +<p>{{lang_assignMachineIntroText}}</p> <h4>{{lang_addNewMachines}}</h4> <form method="post" action="?do=runmode"> @@ -15,13 +10,18 @@ <input type="hidden" name="module" value="{{module}}" id="i-module"> <input type="hidden" name="modeid" value="{{modeid}}" id="i-modeid"> <input type="hidden" name="redirect" value="{{redirect}}"> - <select id="machine-sel" name="machines[]" multiple> + <select id="machine-sel" name="machines[]" multiple {{disabled}}> </select> <div class="buttonbar"> - <button type="submit" class="btn btn-primary">{{lang_save}}</button> + <button type="submit" class="btn btn-primary" {{disabled}}>{{lang_save}}</button> </div> </form> +{{#add-only}} + <h4>{{lang_existingClients}}</h4> + <div id="existing-list"></div> +{{/add-only}} + <script type="application/javascript"><!-- const MODULE = document.getElementById('i-module').value; @@ -58,7 +58,7 @@ function loadMachines(query, callback) { } } $.ajax({ - url: '?do=runmode&action=getmachines&query=' + encodeURIComponent(query), + url: '?do=runmode&action=getmachines&module={{module}}&query=' + encodeURIComponent(query), type: 'GET', dataType: 'json', error: function () { @@ -120,6 +120,17 @@ document.addEventListener('DOMContentLoaded', function () { })(); }); var old = {{{machines}}} || []; + var list = $('#existing-list'); + if (list.length !== 0) { + old.forEach(function (x) { list.append(renderMachineOption(x, function(str) { + return (str + '') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"'); + }))}); + old = []; + } var $box = $('#machine-sel').selectize({ options: old, items: old.map(function(x) { return x.machineuuid; }), @@ -132,7 +143,7 @@ document.addEventListener('DOMContentLoaded', function () { load: loadMachines, maxItems: null, sortField: 'hostname', - sortDirection: 'asc', + sortDirection: 'asc' }); }); diff --git a/modules-available/runmode/templates/module-machine-list.html b/modules-available/runmode/templates/module-machine-list.html index 283fb393..89424922 100644 --- a/modules-available/runmode/templates/module-machine-list.html +++ b/modules-available/runmode/templates/module-machine-list.html @@ -36,12 +36,12 @@ </td> <td class="text-center"> {{#canedit}} - <button type="submit" class="btn btn-danger btn-sm" name="machineuuid" value="{{machineuuid}}"> + <button type="submit" class="btn btn-danger btn-sm" name="machineuuid" value="{{machineuuid}}" {{disabled}}> <span class="glyphicon glyphicon-trash"></span> </button> {{/canedit}} {{#deleteUrl}} - <a class="btn btn-default btn-sm" href="?do={{module}}&{{deleteUrl}}{{modeid}}"> + <a class="btn btn-default btn-sm {{disabled}}" href="?do={{module}}&{{deleteUrl}}{{modeid}}"> <span class="glyphicon glyphicon-edit"></span> </a> {{/deleteUrl}} |