Merge branch 'develop'

pull/107/merge
iglocska 2024-11-28 21:31:48 +01:00
commit 0c320f7d9c
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
14 changed files with 337 additions and 132 deletions

View File

@ -22,20 +22,29 @@ class AuthKeysController extends AppController
{
$currentUser = $this->ACL->getUser();
$conditions = [];
$userId = $this->request->getQuery('Users_id');
if (!empty($userId)) {
$conditions['AND']['Users.id'] = $userId;
}
if (empty($currentUser['role']['perm_community_admin'])) {
$conditions['Users.organisation_id'] = $currentUser['organisation_id'];
if (empty($currentUser['role']['perm_org_admin'])) {
$conditions['Users.id'] = $currentUser['id'];
}
}
$this->CRUD->index([
$indexOptions = [
'filters' => $this->filterFields,
'quickFilters' => $this->quickFilterFields,
'contain' => $this->containFields,
'exclude_fields' => ['authkey'],
'conditions' => $conditions,
'hidden' => []
]);
];
if (!empty($userId)) {
$indexOptions['action_query_strings'] = ['Users.id' => $userId];
}
$this->CRUD->index($indexOptions);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
@ -46,13 +55,7 @@ class AuthKeysController extends AppController
public function delete($id)
{
$currentUser = $this->ACL->getUser();
$conditions = [];
if (empty($currentUser['role']['perm_community_admin'])) {
$conditions['Users.organisation_id'] = $currentUser['organisation_id'];
if (empty($currentUser['role']['perm_org_admin'])) {
$conditions['Users.id'] = $currentUser['id'];
}
}
$conditions = $this->AuthKeys->buildUserConditions($currentUser);
$this->CRUD->delete($id, ['conditions' => $conditions, 'contain' => 'Users']);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
@ -67,21 +70,14 @@ class AuthKeysController extends AppController
$validUsers = [];
$userConditions = [];
$currentUser = $this->ACL->getUser();
if (empty($currentUser['role']['perm_community_admin'])) {
if (empty($currentUser['role']['perm_org_admin'])) {
$userConditions['id'] = $currentUser['id'];
} else {
$role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0, 'perm_community_admin', 'perm_org_admin' => 0])->all()->extract('id')->toList();
$userConditions['organisation_id'] = $currentUser['organisation_id'];
$userConditions['OR'] = [
['role_id IN' => $role_ids],
['id' => $currentUser['id']],
];
}
}
$conditions = $this->AuthKeys->buildUserConditions($currentUser);
$userId = $this->request->getQuery('Users_id');
$users = $this->Users->find('list');
if (!empty($userConditions)) {
$users->where($userConditions);
if (!empty($conditions)) {
$users->where($conditions);
}
if (!empty($userId)) {
$users->where(['Users.id' => $userId]);
}
$users = $users->order(['username' => 'asc'])->all()->toArray();
$this->CRUD->add([

View File

@ -363,11 +363,19 @@ class ACLComponent extends Component
return true;
}
if ($user['role']['perm_community_admin']) {
return false; // org_admins cannot edit admins
$this->Roles = TableRegistry::get('Roles');
$validRoles = [];
if (!$currentUser['role']['perm_community_admin']) {
if ($currentUser['role']['perm_group_admin']) {
$validRoles = $this->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0, 'perm_admin' => 0])->all()->toArray();
} else {
$validRoles = $this->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0, 'perm_org_admin' => 0, 'perm_admin' => 0])->all()->toArray();
}
} else {
$validRoles = $this->Roles->find('list')->order(['name' => 'asc'])->all()->toArray();
}
if ($currentUser['role']['perm_org_admin'] && $user['role']['perm_group_admin']) {
return false; // org_admins cannot edit group_admin
if (!in_array($user['role_id'], array_keys($validRoles)) && $currentUser['id'] != $user['id']) {
return false;
}
if ($currentUser['role']['perm_group_admin']) {
$this->OrgGroups = TableRegistry::get('OrgGroups');
@ -378,9 +386,6 @@ class ACLComponent extends Component
if (!$currentUser['role']['perm_org_admin']) {
return false;
} else {
if ($currentUser['id'] == $user['id']) {
return true;
}
if ($currentUser['organisation_id'] === $user['organisation_id']) {
return true;
}

View File

@ -272,6 +272,10 @@ class CRUDComponent extends Component
$this->Controller->set('model', $this->Table);
$this->Controller->set('data', $data);
$this->Controller->set('embedInModal', $embedInModal);
if (!empty($options['action_query_strings'])) {
$this->Controller->set('action_query_strings', $options['action_query_strings']);
}
$this->Controller->set('skipTableToolbar', $skipTableToolbar);
}
}
@ -465,51 +469,73 @@ class CRUDComponent extends Component
}
}
$data = $this->Table->patchEntity($data, $input, $patchEntityParams);
$break = false;
if (isset($params['beforeSave'])) {
$data = $params['beforeSave']($data);
try {
$data = $params['beforeSave']($data);
} catch (\Exception $e) {
$message = $e->getMessage();
$break = true;
$this->__raiseErrorToUser(__('Could not save {0}.', $this->ObjectAlias), $message);
}
if ($data === false) {
throw new NotFoundException(__('Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.', $this->ObjectAlias));
$break = true;
$this->__raiseErrorToUser(__('Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.', $this->ObjectAlias));
}
}
$savedData = $this->Table->save($data);
if ($savedData !== false) {
if (isset($params['afterSave'])) {
$params['afterSave']($data);
}
$message = __('{0} added.', $this->ObjectAlias);
if ($this->Controller->ParamHandler->isRest()) {
$this->Controller->restResponsePayload = $this->RestResponse->viewData($savedData, 'json');
} else if ($this->Controller->ParamHandler->isAjax()) {
if (!empty($params['displayOnSuccess'])) {
$displayOnSuccess = $this->renderViewInVariable($params['displayOnSuccess'], ['entity' => $data]);
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message, ['displayOnSuccess' => $displayOnSuccess]);
} else {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message);
if (!$break) {
$savedData = $this->Table->save($data);
if ($savedData !== false) {
if (isset($params['afterSave'])) {
try {
$data = $params['afterSave']($data);
} catch (\Exception $e) {
$message = $e->getMessage();
$break = true;
$this->__raiseErrorToUser(__('Saved {0}, but could not execute post-save actions.', $this->ObjectAlias), $message);
}
if ($data === false) {
$break = true;
$this->__raiseErrorToUser(__('Saved {0}, but the post-save actons failed.', $this->ObjectAlias));
}
}
if (!$break) {
$message = __('{0} added.', $this->ObjectAlias);
if ($this->Controller->ParamHandler->isRest()) {
$this->Controller->restResponsePayload = $this->RestResponse->viewData($savedData, 'json');
} else if ($this->Controller->ParamHandler->isAjax()) {
if (!empty($params['displayOnSuccess'])) {
$displayOnSuccess = $this->renderViewInVariable($params['displayOnSuccess'], ['entity' => $data]);
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message, ['displayOnSuccess' => $displayOnSuccess]);
} else {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message);
}
} else {
$this->Controller->Flash->success($message);
if (empty($params['redirect'])) {
$this->Controller->redirect(['action' => 'view', $data->id]);
} else {
$this->Controller->redirect($params['redirect']);
}
}
}
} else {
$this->Controller->Flash->success($message);
if (empty($params['redirect'])) {
$this->Controller->redirect(['action' => 'view', $data->id]);
$this->Controller->isFailResponse = true;
$validationErrors = $data->getErrors();
$validationMessage = $this->prepareValidationMessage($validationErrors);
$message = __(
'{0} could not be added.{1}',
$this->ObjectAlias,
empty($validationMessage) ? '' : PHP_EOL . __('Reason: {0}', $validationMessage)
);
if ($this->Controller->ParamHandler->isRest()) {
$this->Controller->restResponsePayload = $this->RestResponse->viewData($message, 'json');
} else if ($this->Controller->ParamHandler->isAjax()) {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, 'add', $data, $message, $validationErrors);
} else {
$this->Controller->redirect($params['redirect']);
$this->Controller->Flash->error($message);
}
}
} else {
$this->Controller->isFailResponse = true;
$validationErrors = $data->getErrors();
$validationMessage = $this->prepareValidationMessage($validationErrors);
$message = __(
'{0} could not be added.{1}',
$this->ObjectAlias,
empty($validationMessage) ? '' : PHP_EOL . __('Reason: {0}', $validationMessage)
);
if ($this->Controller->ParamHandler->isRest()) {
$this->Controller->restResponsePayload = $this->RestResponse->viewData($message, 'json');
} else if ($this->Controller->ParamHandler->isAjax()) {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, 'add', $data, $message, $validationErrors);
} else {
$this->Controller->Flash->error($message);
}
}
}
if (!empty($params['fields'])) {
@ -723,6 +749,18 @@ class CRUDComponent extends Component
return $input;
}
private function __raiseErrorToUser($title, $message = null)
{
if ($this->Controller->ParamHandler->isRest()) {
$this->Controller->restResponsePayload = $this->RestResponse->viewData($message, 'json');
} else if ($this->Controller->ParamHandler->isAjax()) {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, 'edit', [], $title, ['Custom checks' => ['Custom checks' => $message]]);
} else {
$this->Controller->isFailResponse = true;
$this->Controller->Flash->error($message);
}
}
public function edit(int $id, array $params = []): void
{
if (empty($id)) {
@ -800,54 +838,79 @@ class CRUDComponent extends Component
}
}
$data = $this->Table->patchEntity($data, $input, $patchEntityParams);
$break = false;
if (isset($params['beforeSave'])) {
$data = $params['beforeSave']($data);
try {
$data = $params['beforeSave']($data);
} catch (\Exception $e) {
$message = $e->getMessage();
$break = true;
$this->__raiseErrorToUser(__('Could not save {0}.', $this->ObjectAlias), $message);
}
if ($data === false) {
throw new NotFoundException(__('Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.', $this->ObjectAlias));
$break = true;
$this->__raiseErrorToUser(__('Could not save {0} due to the input failing to meet expectations.', $this->ObjectAlias), __('Your input is bad and you should feel bad.'));
}
}
$savedData = $this->Table->save($data);
if ($savedData !== false) {
if ($metaFieldsEnabled && !empty($metaFieldsToDelete)) {
foreach ($metaFieldsToDelete as $k => $v) {
if ($v === null) {
unset($metaFieldsToDelete[$k]);
if (!$break) {
$savedData = $this->Table->save($data);
if ($savedData !== false) {
if ($metaFieldsEnabled && !empty($metaFieldsToDelete)) {
foreach ($metaFieldsToDelete as $k => $v) {
if ($v === null) {
unset($metaFieldsToDelete[$k]);
}
}
if (!empty($metaFieldsToDelete)) {
$this->Table->MetaFields->unlink($savedData, $metaFieldsToDelete);
}
}
if (!empty($metaFieldsToDelete)) {
$this->Table->MetaFields->unlink($savedData, $metaFieldsToDelete);
$break = false;
if (isset($params['afterSave'])) {
if (isset($params['afterSave'])) {
try {
$data = $params['afterSave']($data);
} catch (\Exception $e) {
$message = $e->getMessage();
$break = true;
$this->__raiseErrorToUser(__('Saved {0} `{1}`, but could not execute post-save actions.', $this->ObjectAlias, $savedData->{$this->Table->getDisplayField()}), $message);
}
if ($data === false) {
$break = true;
$this->__raiseErrorToUser(__('Saved {0} `{1}`, but the post-save actons failed.', $this->ObjectAlias, $savedData->{$this->Table->getDisplayField()}));
}
}
}
if (!$break) {
$message = __('{0} `{1}` updated.', $this->ObjectAlias, $savedData->{$this->Table->getDisplayField()});
if ($this->Controller->ParamHandler->isRest()) {
$this->Controller->restResponsePayload = $this->RestResponse->viewData($savedData, 'json');
} else if ($this->Controller->ParamHandler->isAjax()) {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'edit', $savedData, $message);
} else {
$this->Controller->Flash->success($message);
if (empty($params['redirect'])) {
$this->Controller->redirect(['action' => 'view', $id]);
} else {
$this->Controller->redirect($params['redirect']);
}
}
}
}
if (isset($params['afterSave'])) {
$params['afterSave']($data);
}
$message = __('{0} `{1}` updated.', $this->ObjectAlias, $savedData->{$this->Table->getDisplayField()});
if ($this->Controller->ParamHandler->isRest()) {
$this->Controller->restResponsePayload = $this->RestResponse->viewData($savedData, 'json');
} else if ($this->Controller->ParamHandler->isAjax()) {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'edit', $savedData, $message);
} else {
$this->Controller->Flash->success($message);
if (empty($params['redirect'])) {
$this->Controller->redirect(['action' => 'view', $id]);
$validationErrors = $data->getErrors();
$validationMessage = $this->prepareValidationError($data);
$message = __(
'{0} could not be modified.{1}',
$this->ObjectAlias,
empty($validationMessage) ? '' : PHP_EOL . __('Reason: {0}', $validationMessage)
);
if ($this->Controller->ParamHandler->isRest()) {
} else if ($this->Controller->ParamHandler->isAjax()) {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, 'edit', $data, $message, $validationErrors);
} else {
$this->Controller->redirect($params['redirect']);
$this->Controller->Flash->error($message);
}
}
} else {
$validationErrors = $data->getErrors();
$validationMessage = $this->prepareValidationError($data);
$message = __(
'{0} could not be modified.{1}',
$this->ObjectAlias,
empty($validationMessage) ? '' : PHP_EOL . __('Reason: {0}', $validationMessage)
);
if ($this->Controller->ParamHandler->isRest()) {
} else if ($this->Controller->ParamHandler->isAjax()) {
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, 'edit', $data, $message, $validationErrors);
} else {
$this->Controller->Flash->error($message);
}
}
}
if (!empty($params['fields'])) {
@ -1106,6 +1169,7 @@ class CRUDComponent extends Component
throw new NotFoundException(__('Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.', $this->ObjectAlias));
}
} catch (\Exception $e) {
$exceptionMessage = $e->getMessage();
$entity = false;
}
}
@ -1123,7 +1187,7 @@ class CRUDComponent extends Component
$isBulk,
__('{0} deleted.', $this->ObjectAlias),
__('All selected {0} have been deleted.', Inflector::pluralize($this->ObjectAlias)),
__('Could not delete {0}.', $this->ObjectAlias),
$exceptionMessage ?? __('Could not delete {0}.', $this->ObjectAlias),
__(
'{0} / {1} {2} have been deleted.',
$bulkSuccesses,
@ -1305,7 +1369,7 @@ class CRUDComponent extends Component
if (!empty($additionalData['redirect'])) { // If a redirection occurs, we need to make sure the flash message gets displayed
$this->Controller->Flash->error($message);
}
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, $action, $data, $message, !is_null($errors) ? $errors : $data->getErrors());
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, $action, $data, $message, $errors);
} else {
$this->Controller->Flash->error($message);
$this->Controller->redirect($this->Controller->referer());

View File

@ -440,14 +440,19 @@ class RestResponseComponent extends Component
return $this->viewData($response);
}
public function ajaxFailResponse($ObjectAlias, $action, $entity, $message, $errors = [])
public function ajaxFailResponse($ObjectAlias, $action, $entity = null, $message, $errors = [], $description = '')
{
$action = $this->__dissectAdminRouting($action);
$entity = is_array($entity) ? $entity : $entity->toArray();
if (empty($entity)) {
$entity = [];
} else {
$entity = is_array($entity) ? $entity : $entity->toArray();
}
$response = [
'success' => false,
'message' => $message,
'errors' => $errors,
'description' => $description,
'url' => !empty($entity['id']) ? $this->__generateURL($action, $ObjectAlias, $entity['id']) : ''
];
return $this->viewData($response);

View File

@ -78,7 +78,15 @@ class RolesController extends AppController
public function delete($id)
{
$this->CRUD->delete($id);
$this->CRUD->delete($id, [
'beforeSave' => function ($data) {
$userCount = $this->Roles->Users->find()->where(['role_id' => $data['id']])->count();
if ($userCount > 0) {
throw new ForbiddenException(__('You cannot delete a role that has users assigned to it.'));
}
return true;
}
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;

View File

@ -79,6 +79,10 @@ class UserSettingsController extends AppController
if (empty($currentUser['role']['perm_community_admin'])) {
$data['user_id'] = $currentUser->id;
}
$validationResult = $this->UserSettings->validateUserSetting($data, $currentUser);
if (!$validationResult !== true) {
throw new MethodNotAllowedException(__('You cannot create the given user setting. Reason: {0}', $validationResult));
}
return $data;
}
]);
@ -131,6 +135,10 @@ class UserSettingsController extends AppController
if ($data['user_id'] != $entity->user_id) {
throw new MethodNotAllowedException(__('You cannot assign the setting to a different user.'));
}
$validationResult = $this->UserSettings->validateUserSetting($data);
if ($validationResult !== true) {
throw new MethodNotAllowedException(__('Setting value: {0}', $validationResult));
}
return $data;
}
]);
@ -235,9 +243,10 @@ class UserSettingsController extends AppController
public function saveMyBookmark()
{
if (!$this->request->is('get')) {
$result = $this->UserSettings->saveBookmark($this->ACL->getUser(), $this->request->getData());
$errors = null;
$result = $this->UserSettings->saveBookmark($this->ACL->getUser(), $this->request->getData(), $errors);
$success = !empty($result);
$message = $success ? __('Bookmark saved') : __('Could not save bookmark');
$message = $success ? __('Bookmark saved') : ($errors ?? __('Could not save bookmark'));
$this->CRUD->setResponseForController('saveBookmark', $success, $message, $result);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {

View File

@ -86,10 +86,10 @@ class UsersController extends AppController
$individual_ids = [];
if (!$currentUser['role']['perm_community_admin']) {
if ($currentUser['role']['perm_group_admin']) {
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0])->all()->toArray();
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0, 'perm_admin' => 0])->all()->toArray();
$individual_ids = $this->Users->Individuals->find('aligned', ['organisation_id' => $currentUser['organisation_id']])->all()->extract('id')->toArray();
} else {
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0, 'perm_org_admin' => 0])->all()->toArray();
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0, 'perm_org_admin' => 0, 'perm_admin' => 0])->all()->toArray();
}
if (empty($individual_ids)) {
@ -247,10 +247,10 @@ class UsersController extends AppController
$validOrgIds = [];
if (!$currentUser['role']['perm_community_admin']) {
if ($currentUser['role']['perm_group_admin']) {
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0])->all()->toArray();
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0, 'perm_admin' => 0])->all()->toArray();
$validOrgIds = $this->Users->Organisations->OrgGroups->getGroupOrgIdsForUser($currentUser);
} else {
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0, 'perm_org_admin' => 0])->all()->toArray();
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_community_admin' => 0, 'perm_group_admin' => 0, 'perm_org_admin' => 0, 'perm_admin' => 0])->all()->toArray();
}
} else {
$validRoles = $this->Users->Roles->find('list')->order(['name' => 'asc'])->all()->toArray();
@ -320,10 +320,13 @@ class UsersController extends AppController
}
return $data;
};
$params['beforeSave'] = function ($data) use ($currentUser, $validRoles) {
$params['beforeSave'] = function ($data) use ($currentUser, $validRoles, $validOrgIds) {
if (!in_array($data['role_id'], array_keys($validRoles)) && $this->ACL->getUser()['id'] != $data['id']) {
throw new MethodNotAllowedException(__('You cannot assign the chosen role to a user.'));
}
if (!in_array($data['organisation_id'], $validOrgIds)) {
throw new MethodNotAllowedException(__('You cannot assign the chosen organisation to a user.'));
}
return $data;
};
}
@ -487,7 +490,7 @@ class UsersController extends AppController
{
$editingAnotherUser = false;
$currentUser = $this->ACL->getUser();
if ((empty($currentUser['role']['perm_community_admin']) && empty($currentUser['role']['perm_group_admin'])) || $user_id == $currentUser->id) {
if ((empty($currentUser['role']['perm_community_admin']) && empty($currentUser['role']['perm_group_admin'])) || empty($user_id) || $user_id == $currentUser->id) {
$user = $currentUser;
} else {
$user = $this->Users->get($user_id, [

View File

@ -93,4 +93,36 @@ class AuthKeysTable extends AppTable
}
return [];
}
public function buildUserConditions($currentUser)
{
$conditions = [];
$validOrgs = $this->Users->getValidOrgsForUser($currentUser);
if (empty($currentUser['role']['perm_community_admin'])) {
$conditions['Users.organisation_id IN'] = $validOrgs;
if (empty($currentUser['role']['perm_group_admin'])) {
if (empty($currentUser['role']['perm_org_admin'])) {
$conditions['Users.id'] = $currentUser['id'];
} else {
$role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0, 'perm_community_admin' => 0, 'perm_org_admin' => 0, 'perm_group_admin' => 0])->all()->extract('id')->toList();
$conditions['Users.organisation_id'] = $currentUser['organisation_id'];
$subConditions = [
['Users.id' => $currentUser['id']]
];
if (!empty($role_ids)) {
$subConditions[] = ['Users.role_id IN' => $role_ids];
}
$conditions['OR'] = $subConditions;
}
} else {
$conditions['Users.group_id'] = $currentUser['group_id'];
$role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0, 'perm_community_admin' => 0, 'perm_group_admin' => 0])->all()->extract('id')->toList();
$conditions['OR'] = [
['Users.id' => $currentUser['id']],
['Users.role_id IN' => $role_ids]
];
}
}
return $conditions;
}
}

View File

@ -163,18 +163,22 @@ class BaseSettingsProvider
$setting['error'] = false;
if (!$skipValidation) {
$validationResult = true;
if (!isset($setting['value'])) {
$validationResult = $this->settingValidator->testEmptyBecomesDefault(null, $setting);
} else if (isset($setting['test'])) {
$setting['value'] = $setting['value'] ?? '';
$validationResult = $this->evaluateFunctionForSetting($setting['test'], $setting);
}
if ($validationResult !== true) {
$setting['severity'] = $setting['severity'] ?? 'warning';
if (!in_array($setting['severity'], $this->severities)) {
$setting['severity'] = 'warning';
if (empty($setting['value']) && !empty($setting['empty'])) {
$validationResult = true;
} else {
if (!isset($setting['value'])) {
$validationResult = $this->settingValidator->testEmptyBecomesDefault(null, $setting);
} else if (isset($setting['test'])) {
$setting['value'] = $setting['value'] ?? '';
$validationResult = $this->evaluateFunctionForSetting($setting['test'], $setting);
}
if ($validationResult !== true) {
$setting['severity'] = $setting['severity'] ?? 'warning';
if (!in_array($setting['severity'], $this->severities)) {
$setting['severity'] = 'warning';
}
$setting['errorMessage'] = $validationResult;
}
$setting['errorMessage'] = $validationResult;
}
$setting['error'] = $validationResult !== true ? true : false;
}

View File

@ -136,15 +136,19 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
'Proxy' => [
'Proxy.host' => [
'name' => __('Host'),
'severity' => 'info',
'type' => 'string',
'description' => __('The hostname of an HTTP proxy for outgoing sync requests. Leave empty to not use a proxy.'),
'test' => 'testHostname',
'empty' => true
],
'Proxy.port' => [
'name' => __('Port'),
'severity' => 'info',
'type' => 'integer',
'description' => __('The TCP port for the HTTP proxy.'),
'test' => 'testForRangeXY',
'empty' => true
],
'Proxy.user' => [
'name' => __('User'),
@ -152,6 +156,7 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
'description' => __('The authentication username for the HTTP proxy.'),
'default' => 'admin',
'dependsOn' => 'proxy.host',
'empty' => true
],
'Proxy.password' => [
'name' => __('Password'),
@ -159,6 +164,7 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
'description' => __('The authentication password for the HTTP proxy.'),
'default' => '',
'dependsOn' => 'proxy.host',
'empty' => true
],
],
],
@ -338,6 +344,17 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
],
]
],
'Restrictions' => [
'Allowed bookmark domains' => [
'security.restrictions.allowed_bookmark_domains' => [
'name' => __('Allowed bookmark domains'),
'type' => 'string',
'severity' => 'info',
'description' => __('Comma separated list of allowed bookmark domains. Leave empty to allow all domains.'),
'default' => '',
],
],
],
'Development' => [
'Debugging' => [
'debug' => [

View File

@ -5,6 +5,7 @@ namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Core\Configure;
require_once(APP . 'Model' . DS . 'Table' . DS . 'SettingProviders' . DS . 'UserSettingsProvider.php');
use App\Settings\SettingsProvider\UserSettingsProvider;
@ -94,14 +95,37 @@ class UserSettingsTable extends AppTable
return $savedData;
}
public function saveBookmark($user, $data)
public function saveBookmark($user, $data, &$message = null)
{
$setting = $this->getSettingByName($user, $this->BOOKMARK_SETTING_NAME);
$fieldsToCheck = [
'bookmark_label' => __('Label'),
'bookmark_name' => __('Name'),
'bookmark_url' => __('URL')
];
foreach ($fieldsToCheck as $field => $fieldName) {
if (empty($data[$field])) {
$message = __('Please fill in all fields, {0} missing.', $fieldName);
return null;
}
}
if (empty($data['bookmark_label']) || empty($data['bookmark_name']) || empty($data['bookmark_url'])) {
return null;
}
$bookmarkData = [
'label' => $data['bookmark_label'],
'name' => $data['bookmark_name'],
'url' => $data['bookmark_url'],
];
$restricted_domains = Configure::read('security.restrictions.allowed_bookmark_domains');
if (!empty($restricted_domains)) {
$restricted_domains = explode(',', $restricted_domains);
$parsed = parse_url($bookmarkData['url']);
if (!empty($parsed['host']) && !in_array($parsed['host'], $restricted_domains)) {
return null;
}
}
if (is_null($setting)) { // setting not found, create it
$bookmarksData = json_encode([$bookmarkData]);
$result = $this->createSetting($user, $this->BOOKMARK_SETTING_NAME, $bookmarksData);
@ -153,4 +177,40 @@ class UserSettingsTable extends AppTable
}
return $isLocalPath || $isValidURL;
}
public function validateUserSetting($data): bool|string
{
$errors = [];
$json_fields = ['ui.bookmarks'];
if (in_array($data['name'], $json_fields)) {
$decoded = json_decode($data['value'], true);
if (json_last_error() !== JSON_ERROR_NONE) {
return __('Invalid JSON data');
}
$value = $decoded;
} else {
$value = $data['value'];
}
if ($data['name'] === 'ui.bookmarks') {
if (array_values($value) !== $value) {
$value = [$value];
}
$restricted_domains = Configure::read('security.restrictions.allowed_bookmark_domains');
if (!empty($restricted_domains)) {
$restricted_domains = explode(',', $restricted_domains);
foreach ($restricted_domains as &$rd) {
$rd = trim($rd);
}
}
foreach ($value as $bookmark) {
if (!empty($restricted_domains)) {
$parsed = parse_url($bookmark['url']);
if (!in_array($parsed['host'], $restricted_domains)) {
return __('Invalid domain for bookmark. The only domains allowed are: {0}', implode(', ', $restricted_domains));
}
}
}
}
return true;
}
}

View File

@ -221,7 +221,9 @@ class UsersTable extends AppTable
'perm_admin' => 1,
'perm_community_admin' => 1,
'perm_org_admin' => 1,
'perm_sync' => 1
'perm_sync' => 1,
'perm_group_admin' => 1,
'perm_meta_field_editor' => 1
]);
$this->Roles->save($role);
$this->Organisations = TableRegistry::get('Organisations');

View File

@ -1,4 +1,4 @@
{
"version": "1.25",
"version": "1.26",
"application": "Cerebrate"
}

View File

@ -10,8 +10,8 @@ echo $this->element('genericElements/IndexTable/index_table', [
'data' => [
'type' => 'simple',
'text' => __('Add authentication key'),
'popover_url' => '/authKeys/add',
'reload_url' => $this->request->getRequestTarget()
'popover_url' => '/authKeys/add' . ($action_query_strings ? '?' . http_build_query($action_query_strings) : ''),
'reload_url' => $this->request->getRequestTarget(),
]
]
],