MISP/app/Controller/UserSettingsController.php

406 lines
17 KiB
PHP

<?php
/*
*
* Feature developed as part of a training given by CIRCL in Luxembourg on 26/09/2019
* Verbose comments for educational purposes only
*
*/
App::uses('AppController', 'Controller');
/**
* @property UserSetting $UserSetting
*/
class UserSettingsController extends AppController
{
public $components = array('Session', 'RequestHandler');
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999,
'order' => array(
'UserSetting.id' => 'DESC'
),
'contain' => array(
'User.id',
'User.email'
)
);
public function beforeFilter()
{
parent::beforeFilter();
$this->Security->unlockedActions[] = 'eventIndexColumnToggle';
}
public function index()
{
$filterData = array(
'request' => $this->request,
'paramArray' => array('setting', 'user_id', 'sort', 'direction', 'page', 'limit'),
'named_params' => $this->params['named']
);
$exception = false;
$filters = $this->_harvestParameters($filterData, $exception);
$conditions = array();
if (!empty($filters['setting'])) {
$conditions['AND'][] = array(
'setting' => $filters['setting']
);
}
if (!empty($filters['user_id'])) {
if ($filters['user_id'] === 'all') {
$context = 'all';
} else if ($filters['user_id'] === 'me') {
$conditions['AND'][] = array(
'user_id' => $this->Auth->user('id')
);
$context = 'me';
} else if ($filters['user_id'] === 'org') {
$conditions['AND'][] = array(
'user_id' => $this->UserSetting->User->find(
'list', array(
'conditions' => array(
'User.org_id' => $this->Auth->user('org_id')
),
'fields' => array(
'User.id', 'User.id'
)
)
)
);
$context = 'org';
} else {
$conditions['AND'][] = array(
'user_id' => $filters['user_id']
);
}
}
if (!$this->_isSiteAdmin()) {
if ($this->_isAdmin()) {
$conditions['AND'][] = array(
'UserSetting.user_id' => $this->UserSetting->User->find(
'list', array(
'conditions' => array(
'User.org_id' => $this->Auth->user('org_id')
),
'fields' => array(
'User.id', 'User.id'
)
)
)
);
} else {
$conditions['AND'][] = array(
'UserSetting.user_id' => $this->Auth->user('id')
);
}
}
// Do not show internal settings
if (!$this->_isSiteAdmin()) {
$conditions['AND'][] = ['NOT' => ['UserSetting.setting' => $this->UserSetting->getInternalSettingNames()]];
}
if ($this->_isRest()) {
$params = array(
'conditions' => $conditions
);
if (!empty($filters['page'])) {
$params['page'] = $filters['page'];
$params['limit'] = $this->paginate['limit'];
}
if (!empty($filters['limit'])) {
$params['limit'] = $filters['limit'];
}
$userSettings = $this->UserSetting->find('all', $params);
return $this->RestResponse->viewData($userSettings, $this->response->type());
} else {
$this->paginate['conditions'] = $conditions;
$data = $this->paginate();
foreach ($data as $k => $v) {
if (!empty(UserSetting::VALID_SETTINGS[$v['UserSetting']['setting']])) {
$data[$k]['UserSetting']['restricted'] = empty(UserSetting::VALID_SETTINGS[$v['UserSetting']['setting']]['restricted']) ? '' : UserSetting::VALID_SETTINGS[$v['UserSetting']['setting']]['restricted'];
} else {
$data[$k]['UserSetting']['restricted'] = array();
}
}
$this->set('data', $data);
$this->set('context', empty($context) ? 'null' : $context);
}
}
public function view($id)
{
if (!$this->_isRest()) {
throw new BadRequestException("This endpoint is accessible just by REST requests.");
}
// check if the ID is valid and whether a user setting with the given ID exists
if (empty($id) || !is_numeric($id)) {
throw new BadRequestException(__('Invalid ID passed.'));
}
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => array(
'UserSetting.id' => $id
),
'contain' => array('User.id', 'User.org_id')
));
if (empty($userSetting)) {
throw new NotFoundException(__('Invalid user setting.'));
}
$checkAccess = $this->UserSetting->checkAccess($this->Auth->user(), $userSetting);
if (!$checkAccess) {
throw new NotFoundException(__('Invalid user setting.'));
}
unset($userSetting['User']);
return $this->RestResponse->viewData($userSetting, $this->response->type());
}
public function setSetting($user_id = false, $setting = false)
{
if (!empty($setting)) {
if (!$this->UserSetting->checkSettingValidity($setting) || $this->UserSetting->isInternal($setting)) {
throw new MethodNotAllowedException(__('Invalid setting.'));
}
$settingPermCheck = $this->UserSetting->checkSettingAccess($this->Auth->user(), $setting);
if ($settingPermCheck !== true) {
throw new MethodNotAllowedException(__('This setting is restricted and requires the following permission(s): %s', $settingPermCheck));
}
}
// handle POST requests
if ($this->request->is('post')) {
// massage the request to allow for unencapsulated POST requests via the API
// {"key": "value"} instead of {"UserSetting": {"key": "value"}}
if (empty($this->request->data['UserSetting'])) {
$this->request->data = array('UserSetting' => $this->request->data);
}
if (!empty($user_id)) {
$this->request->data['UserSetting']['user_id'] = $user_id;
}
if (!empty($setting)) {
$this->request->data['UserSetting']['setting'] = $setting;
}
$result = $this->UserSetting->setSetting($this->Auth->user(), $this->request->data);
if ($result) {
// if we've managed to save our setting
if ($this->_isRest()) {
// if we are dealing with an API request
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => array('UserSetting.id' => $this->UserSetting->id)
));
return $this->RestResponse->viewData($userSetting, $this->response->type());
} else {
// if we are dealing with a UI request, redirect the user to the user view with the proper flash message
$this->Flash->success(__('Setting saved.'));
$this->redirect(array('controller' => 'user_settings', 'action' => 'index', $this->Auth->User('id')));
}
} else {
// if we've failed saving our setting
if ($this->_isRest()) {
// if we are dealing with an API request
return $this->RestResponse->saveFailResponse('UserSettings', 'add', false, $this->UserSetting->validationErrors, $this->response->type());
} else {
/*
* if we are dealing with a UI request, simply set an error in a flash message
* and render the view of this endpoint, pre-populated with the submitted values.
*/
$this->Flash->error(__('Setting could not be saved.'));
}
}
}
if ($this->_isRest()) {
// GET request via the API should describe the endpoint
return $this->RestResponse->describe('UserSettings', 'setSetting', false, $this->response->type());
}
// load the valid settings from the model
if ($this->_isSiteAdmin()) {
$users = $this->UserSetting->User->find('list', array(
'fields' => array('User.id', 'User.email')
));
} else if ($this->_isAdmin()) {
$users = $this->UserSetting->User->find('list', array(
'conditions' => array('User.org_id' => $this->Auth->user('org_id')),
'fields' => array('User.id', 'User.email')
));
} else {
$users = array($this->Auth->user('id') => $this->Auth->user('email'));
}
if (!empty($user_id) && $this->request->is('get')) {
$this->request->data['UserSetting']['user_id'] = $user_id;
}
$this->set('setting', $setting);
$this->set('users', $users);
$this->set('validSettings', $this->UserSetting->settingPlaceholders($this->Auth->user()));
$this->set('title_for_layout', __('Set User Setting'));
}
public function getSetting($userId = null, $setting = null)
{
if ($this->request->is('post')) {
if (empty($this->request->data['setting'])) {
throw new BadRequestException("No setting name provided.");
}
$setting = $this->request->data['setting'];
$userId = $this->request->data['user_id'] ?? $this->Auth->user('id');
} else {
if (empty($userId) || empty($setting)) {
throw new BadRequestException("No setting name or user ID provided.");
}
}
if (!$this->UserSetting->checkSettingValidity($setting) || $this->UserSetting->isInternal($setting)) {
throw new NotFoundException(__('Invalid setting.'));
}
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => [
'UserSetting.user_id' => $userId,
'UserSetting.setting' => $setting,
],
'contain' => array('User.id', 'User.org_id')
));
if (empty($userSetting)) {
throw new NotFoundException(__('Invalid setting.'));
}
$checkAccess = $this->UserSetting->checkAccess($this->Auth->user(), $userSetting, $userId);
if (!$checkAccess) {
throw new NotFoundException(__('Invalid setting.'));
}
return $this->RestResponse->viewData($userSetting['UserSetting'], $this->response->type());
}
public function delete($id = false)
{
if ($this->request->is('get') && $this->_isRest()) {
/*
* GET request via the API should describe the endpoint
* Unlike with the add() endpoint, we want to run this check before doing anything else,
* in order to allow us to reach this endpoint without passing a valid ID
*/
return $this->RestResponse->describe('UserSettings', 'delete', false, $this->response->type());
}
if (!$this->request->is('post') && !$this->request->is('delete')) {
throw new MethodNotAllowedException(__('Expecting POST or DELETE request.'));
}
if (empty($id)) {
if (empty($this->request->data['setting'])) {
throw new BadRequestException("No setting name to delete provided.");
}
$conditions = ['UserSetting.setting' => $this->request->data['setting']];
if (!empty($this->request->data['user_id'])) {
$conditions['UserSetting.user_id'] = $this->request->data['user_id'];
} else {
$conditions['UserSetting.user_id'] = $this->Auth->user('id'); // current user
}
} else if (is_numeric($id)) {
$conditions = ['UserSetting.id' => $id];
} else {
throw new BadRequestException(__('Invalid ID passed.'));
}
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => $conditions,
'contain' => array('User.id', 'User.org_id')
));
if (empty($userSetting)) {
throw new NotFoundException(__('Invalid user setting.'));
}
$checkAccess = $this->UserSetting->checkAccess($this->Auth->user(), $userSetting);
if (!$checkAccess) {
throw new NotFoundException(__('Invalid user setting.'));
}
$settingPermCheck = $this->UserSetting->checkSettingAccess($this->Auth->user(), $userSetting['UserSetting']['setting']);
if ($settingPermCheck !== true) {
throw new MethodNotAllowedException(__('This setting is restricted and requires the following permission(s): %s', $settingPermCheck));
}
// Delete the setting that we were after.
$result = $this->UserSetting->delete($userSetting['UserSetting']['id']);
if ($result) {
// set the response for both the UI and API
$message = __('Setting deleted.');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('UserSettings', 'delete', $userSetting['UserSetting']['id'], $this->response->type(), $message);
} else {
$this->Flash->success($message);
}
} else {
// set the response for both the UI and API
$message = __('Setting could not be deleted.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('UserSettings', 'delete', $userSetting['UserSetting']['id'], $message, $this->response->type());
} else {
$this->Flash->error($message);
}
}
/*
* The API responses stopped executing this function and returned a serialised response to the user.
* For UI users, redirect to where they issued the request from.
*/
$this->redirect($this->referer());
}
public function setHomePage()
{
if ($this->request->is('post')) {
if (isset($this->request->data['UserSetting'])) {
$this->request->data = $this->request->data['UserSetting'];
}
if (!isset($this->request->data['path'])) {
$this->request->data = array('path' => $this->request->data);
}
if (empty($this->request->data['path'])) {
throw new InvalidArgumentException(__('No path POSTed.'));
}
$setting = array(
'UserSetting' => array(
'user_id' => $this->Auth->user('id'),
'setting' => 'homepage',
'value' => ['path' => $this->request->data['path']],
)
);
$result = $this->UserSetting->setSetting($this->Auth->user(), $setting);
return $this->RestResponse->saveSuccessResponse('UserSettings', 'setHomePage', false, 'json', 'Homepage set to ' . $this->request->data['path']);
} else {
$this->layout = false;
}
}
public function eventIndexColumnToggle($columnName)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('Expecting POST request.'));
}
$hideColumns = $this->UserSetting->getValueForUser($this->Auth->user()['id'], 'event_index_hide_columns');
if ($hideColumns === null) {
$hideColumns = [];
}
if (($key = array_search($columnName, $hideColumns, true)) !== false) {
unset($hideColumns[$key]);
$hideColumns = array_values($hideColumns);
} else {
$hideColumns[] = $columnName;
}
$setting = [
'UserSetting' => [
'user_id' => $this->Auth->user()['id'],
'setting' => 'event_index_hide_columns',
'value' => $hideColumns,
]
];
$this->UserSetting->setSetting($this->Auth->user(), $setting);
return $this->RestResponse->saveSuccessResponse('UserSettings', 'eventIndexColumnToggle', false, 'json', 'Column visibility switched');
}
}