From c3a96c27af149cd3ad07ce7f67f41e8a36ab5fc6 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 13 Jan 2021 14:30:44 +0100 Subject: [PATCH] new: [broods] added - Cerebrate <-> Cerebrate sync - explore remote cerebrate instances - fetch data from remote (orgs, individuals) - run connection tests with a remote instance - check the version, sync user privileges on the remote --- src/Controller/BroodsController.php | 119 +++++++++++++++++++ src/Model/Table/BroodsTable.php | 120 ++++++++++++++++++++ templates/Broods/add.php | 53 +++++++++ templates/Broods/index.php | 83 ++++++++++++++ templates/Broods/preview_individuals.php | 65 +++++++++++ templates/Broods/preview_organisations.php | 70 ++++++++++++ templates/Broods/preview_sharing_groups.php | 50 ++++++++ templates/Broods/view.php | 49 ++++++++ 8 files changed, 609 insertions(+) create mode 100644 src/Controller/BroodsController.php create mode 100644 src/Model/Table/BroodsTable.php create mode 100644 templates/Broods/add.php create mode 100644 templates/Broods/index.php create mode 100644 templates/Broods/preview_individuals.php create mode 100644 templates/Broods/preview_organisations.php create mode 100644 templates/Broods/preview_sharing_groups.php create mode 100644 templates/Broods/view.php diff --git a/src/Controller/BroodsController.php b/src/Controller/BroodsController.php new file mode 100644 index 0000000..d9d2cd8 --- /dev/null +++ b/src/Controller/BroodsController.php @@ -0,0 +1,119 @@ +CRUD->index([ + 'filters' => ['name', 'uuid', 'url', 'description', 'Organisations.id', 'trusted', 'pull', 'authkey'], + 'quickFilters' => ['name', 'uuid', 'description'], + 'contain' => ['Organisations'] + ]); + if ($this->ParamHandler->isRest()) { + return $this->restResponsePayload; + } + $this->set('metaGroup', 'Sync'); + } + + public function add() + { + $this->CRUD->add(); + if ($this->ParamHandler->isRest()) { + return $this->restResponsePayload; + } + $this->set('metaGroup', 'Sync'); + $this->loadModel('Organisations'); + $dropdownData = [ + 'organisation' => $this->Organisations->find('list', [ + 'sort' => ['name' => 'asc'] + ]) + ]; + $this->set(compact('dropdownData')); + } + + public function view($id) + { + $this->CRUD->view($id, ['contain' => ['Organisations']]); + if ($this->ParamHandler->isRest()) { + return $this->restResponsePayload; + } + $this->set('metaGroup', 'Sync'); + } + + public function edit($id) + { + $this->CRUD->edit($id); + if ($this->ParamHandler->isRest()) { + return $this->restResponsePayload; + } + $this->set('metaGroup', 'Sync'); + $this->render('add'); + } + + public function delete($id) + { + $this->CRUD->delete($id); + if ($this->ParamHandler->isRest()) { + return $this->restResponsePayload; + } + $this->set('metaGroup', 'Sync'); + } + + public function testConnection($id) + { + $status = $this->Broods->queryStatus($id); + return $this->RestResponse->viewData($status, 'json'); + } + + public function previewIndex($id, $scope) + { + if (!in_array($scope, ['organisations', 'individuals', 'sharingGroups'])) { + throw new MethodNotAllowedException(__('Invalid scope. Valid options are: organisations, individuals, sharing_groups')); + } + $filter = $this->request->getQuery('quickFilter'); + $data = $this->Broods->queryIndex($id, $scope, $filter); + if (!is_array($data)) { + $data = []; + } + if ($this->ParamHandler->isRest()) { + return $this->RestResponse->viewData($data, 'json'); + } else { + $data = $this->CustomPagination->paginate($data); + $this->set('data', $data); + $this->set('brood_id', $id); + if ($this->request->is('ajax')) { + $this->viewBuilder()->disableAutoLayout(); + } + $this->set('metaGroup', 'Sync'); + $this->render('preview_' . $scope); + } + } + + public function downloadOrg($brood_id, $org_id) + { + $result = $this->Broods->downloadOrg($brood_id, $org_id); + $success = __('Organisation fetched from remote.'); + $fail = __('Could not save the remote organisation'); + if ($this->ParamHandler->isRest()) { + if ($result) { + return $this->RestResponse->saveSuccessResponse('Brood', 'downloadOrg', $brood_id, 'json', $success); + } else { + return $this->RestResponse->saveFailResponse('Brood', 'downloadOrg', $brood_id, $fail, 'json'); + } + } else { + if ($result) { + $this->Flash->success($success); + } else { + $this->Flash->error($fail); + } + $this->redirect($this->referer()); + } + } +} diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php new file mode 100644 index 0000000..51424f1 --- /dev/null +++ b/src/Model/Table/BroodsTable.php @@ -0,0 +1,120 @@ +addBehavior('UUID'); + $this->BelongsTo( + 'Organisations' + ); + $this->setDisplayField('name'); + } + + public function validationDefault(Validator $validator): Validator + { + return $validator; + } + + public function queryStatus($id) + { + $brood = $this->find()->where(['id' => $id])->first(); + $http = new Client(); + $start = microtime(true); + $response = $http->get($brood['url'] . '/instance/status.json', [], [ + 'headers' => [ + 'Authorization' => $brood['authkey'], + 'Accept' => 'Application/json', + 'Content-type' => 'Application/json' + ] + ]); + $ping = ((int)(100 * (microtime(true) - $start))); + $errors = [ + 403 => [ + 'error' => __('Authentication failure'), + 'reason' => __('Invalid user credentials.') + ], + 405 => [ + 'error' => __('Insufficient privileges'), + 'reason' => __('The remote user account doesn\'t have the required privileges to synchronise.') + ], + 500 => [ + 'error' => __('Internal error'), + 'reason' => __('Something is probably broken on the remote side. Get in touch with the instance owner.') + ] + ]; + $result = [ + 'code' => $response->getStatusCode() + ]; + if ($response->isOk()) { + $raw = $response->getJson(); + $result['response']['role'] = $raw['user']['role']; + $result['response']['user'] = $raw['user']['username']; + $result['response']['application'] = $raw['application']; + $result['response']['version'] = $raw['version']; + $result['ping'] = $ping; + } else { + $result['error'] = $errors[$result['code']]['error']; + $result['reason'] = $errors[$result['code']]['reason']; + $result['ping'] = $ping; + } + return $result; + } + + public function queryIndex($id, $scope, $filter) + { + $brood = $this->find()->where(['id' => $id])->first(); + if (empty($brood)) { + throw new NotFoundException(__('Brood not found')); + } + $http = new Client(); + $filterQuery = empty($filter) ? '' : '?quickFilter=' . urlencode($filter); + $response = $http->get($brood['url'] . '/' . $scope . '/index.json' . $filterQuery , [], [ + 'headers' => [ + 'Authorization' => $brood['authkey'], + 'Accept' => 'Application/json', + 'Content-type' => 'Application/json' + ] + ]); + if ($response->isOk()) { + return $response->getJson(); + } else { + return false; + } + } + + public function downloadOrg($brood_id, $org_id) + { + $query = $this->find(); + $brood = $query->where(['id' => $brood_id])->first(); + if (empty($brood)) { + throw new NotFoundException(__('Brood not found')); + } + $http = new Client(); + $response = $http->get($brood['url'] . '/organisations/view/' . $org_id . '/index.json' , [], [ + 'headers' => [ + 'Authorization' => $brood['authkey'], + 'Accept' => 'Application/json', + 'Content-type' => 'Application/json' + ] + ]); + if ($response->isOk()) { + $org = $response->getJson(); + $this->Organisation = TableRegistry::getTableLocator()->get('Organisations'); + $result = $this->Organisation->captureOrg($org); + return $result; + } else { + return false; + } + } +} diff --git a/templates/Broods/add.php b/templates/Broods/add.php new file mode 100644 index 0000000..ffd525f --- /dev/null +++ b/templates/Broods/add.php @@ -0,0 +1,53 @@ +element('genericElements/Form/genericForm', array( + 'data' => array( + 'description' => __('Individuals are natural persons. They are meant to describe the basic information about an individual that may or may not be a user of this community. Users in genral require an individual object to identify the person behind them - however, no user account is required to store information about an individual. Individuals can have affiliations to organisations and broods as well as cryptographic keys, using which their messages can be verified and which can be used to securely contact them.'), + 'model' => 'Organisations', + 'fields' => array( + array( + 'field' => 'name' + ), + array( + 'field' => 'url', + 'label' => __('URL') + ), + array( + 'field' => 'authkey', + 'label' => 'Authkey', + 'type' => 'authkey', + 'default' => '' + ), + array( + 'field' => 'description', + 'type' => 'textarea' + ), + array( + 'field' => 'organisation_id', + 'label' => __('Owner organisation'), + 'options' => $dropdownData['organisation'], + 'type' => 'dropdown' + ), + array( + 'field' => 'trusted', + 'label' => __('Trusted upstream source'), + 'type' => 'checkbox' + ), + array( + 'field' => 'pull', + 'label' => __('Enable pulling of trust information'), + 'type' => 'checkbox' + ), + array( + 'field' => 'skip_proxy', + 'label' => 'Skip proxy', + 'type' => 'checkbox' + ) + ), + 'submit' => array( + 'action' => $this->request->getParam('action') + ) + ) + )); +?> + diff --git a/templates/Broods/index.php b/templates/Broods/index.php new file mode 100644 index 0000000..9a3e103 --- /dev/null +++ b/templates/Broods/index.php @@ -0,0 +1,83 @@ +element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'data' => $data, + 'top_bar' => [ + 'pull' => 'right', + 'children' => [ + [ + 'type' => 'simple', + 'children' => [ + 'data' => [ + 'type' => 'simple', + 'text' => __('Add brood'), + 'class' => 'btn btn-primary', + 'popover_url' => '/broods/add' + ] + ] + ], + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value' + ] + ] + ], + 'fields' => [ + [ + 'name' => '#', + 'sort' => 'id', + 'data_path' => 'id', + ], + [ + 'name' => __('Name'), + 'sort' => 'name', + 'data_path' => 'name', + ], + [ + 'name' => 'Connection test', + 'data_path' => 'id', + 'element' => 'connection_test' + ], + [ + 'name' => __('Url'), + 'sort' => 'url', + 'data_path' => 'url', + ], + [ + 'name' => __('Description'), + 'data_path' => 'description', + ], + [ + 'name' => __('Owner Organisation'), + 'sort' => 'organisation.name', + 'data_path' => 'organisation', + 'element' => 'org' + ] + ], + 'title' => __('Broods Index'), + 'description' => __('Cerebrate can connect to other Cerebrate instances to exchange trust information and to instrument interconnectivity between connected local tools. Each such Cerebrate instance with its connected tools is considered to be a brood.'), + 'pull' => 'right', + 'actions' => [ + [ + 'url' => '/broods/view', + 'url_params_data_paths' => ['id'], + 'icon' => 'eye' + ], + [ + 'onclick' => 'populateAndLoadModal(\'/broods/edit/[onclick_params_data_path]\');', + 'onclick_params_data_path' => 'id', + 'icon' => 'edit' + ], + [ + 'onclick' => 'populateAndLoadModal(\'/broods/delete/[onclick_params_data_path]\');', + 'onclick_params_data_path' => 'id', + 'icon' => 'trash' + ] + ] + ] +]); +echo ''; +?> diff --git a/templates/Broods/preview_individuals.php b/templates/Broods/preview_individuals.php new file mode 100644 index 0000000..85a565d --- /dev/null +++ b/templates/Broods/preview_individuals.php @@ -0,0 +1,65 @@ +element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'data' => $data, + 'top_bar' => [ + 'pull' => 'right', + 'children' => [ + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'additionalUrlParams' => $brood_id . '/individuals' + ] + ] + ], + 'fields' => [ + [ + 'name' => '#', + 'sort' => 'id', + 'data_path' => 'id', + ], + [ + 'name' => __('Email'), + 'sort' => 'email', + 'data_path' => 'email', + ], + [ + 'name' => __('First Name'), + 'sort' => 'first_name', + 'data_path' => 'first_name', + ], + [ + 'name' => __('Last Name'), + 'sort' => 'last_name', + 'data_path' => 'last_name', + ], + [ + 'name' => __('Alignments'), + 'data_path' => 'alignments', + 'element' => 'alignments', + 'scope' => 'organisation' + ], + [ + 'name' => __('UUID'), + 'sort' => 'uuid', + 'data_path' => 'uuid', + 'placeholder' => __('Leave empty to auto generate') + ], + ], + 'title' => __('Individuals Index'), + 'pull' => 'right', + 'actions' => [ + [ + 'url' => '/broods/downloadIndividual/' . $brood_id, + 'url_params_data_paths' => ['id'], + 'title' => __('Download'), + 'icon' => 'download' + ] + ] + ] +]); +echo ''; +?> diff --git a/templates/Broods/preview_organisations.php b/templates/Broods/preview_organisations.php new file mode 100644 index 0000000..5bded24 --- /dev/null +++ b/templates/Broods/preview_organisations.php @@ -0,0 +1,70 @@ +element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'data' => $data, + 'top_bar' => [ + 'pull' => 'right', + 'children' => [ + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'additionalUrlParams' => $brood_id . '/organisations' + ] + ] + ], + 'fields' => [ + [ + 'name' => '#', + 'sort' => 'id', + 'class' => 'short', + 'data_path' => 'id', + ], + [ + 'name' => __('Name'), + 'class' => 'short', + 'data_path' => 'name', + 'sort' => 'name' + ], + [ + 'name' => __('UUID'), + 'class' => 'short', + 'data_path' => 'uuid', + ], + [ + 'name' => __('URL'), + 'class' => 'short', + 'data_path' => 'url', + ], + [ + 'name' => __('Nationality'), + 'data_path' => 'nationality', + 'sort' => 'nationality' + ], + [ + 'name' => __('Sector'), + 'data_path' => 'sector', + 'sort' => 'sector' + ], + [ + 'name' => __('Type'), + 'data_path' => 'type', + 'sort' => 'type' + ] + ], + 'title' => __('Organisation Index'), + 'pull' => 'right', + 'actions' => [ + [ + 'url' => '/broods/downloadOrg/' . $brood_id, + 'url_params_data_paths' => ['id'], + 'title' => __('Download'), + 'icon' => 'download' + ] + ] + ] +]); +echo ''; +?> diff --git a/templates/Broods/preview_sharing_groups.php b/templates/Broods/preview_sharing_groups.php new file mode 100644 index 0000000..1348182 --- /dev/null +++ b/templates/Broods/preview_sharing_groups.php @@ -0,0 +1,50 @@ +element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'data' => $data, + 'top_bar' => [ + 'pull' => 'right', + 'children' => [ + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'additionalUrlParams' => $brood_id . '/sharingGroups' + ] + ] + ], + 'fields' => [ + [ + 'name' => '#', + 'sort' => 'id', + 'class' => 'short', + 'data_path' => 'id', + ], + [ + 'name' => __('Name'), + 'class' => 'short', + 'data_path' => 'name', + ], + [ + 'name' => __('UUID'), + 'sort' => 'uuid', + 'class' => 'short', + 'data_path' => 'uuid', + ] + ], + 'title' => __('Sharing Groups Index'), + 'pull' => 'right', + 'actions' => [ + [ + 'url' => '/broods/downloadSharingGroup/' . $brood_id, + 'url_params_data_paths' => ['id'], + 'title' => __('Download'), + 'icon' => 'download' + ] + ] + ] +]); +echo ''; +?> diff --git a/templates/Broods/view.php b/templates/Broods/view.php new file mode 100644 index 0000000..5a0c03e --- /dev/null +++ b/templates/Broods/view.php @@ -0,0 +1,49 @@ +element( + '/genericElements/SingleViews/single_view', + [ + 'title' => __('Cererate Brood View'), + 'data' => $entity, + 'fields' => [ + [ + 'key' => __('ID'), + 'path' => 'id' + ], + [ + 'key' => __('Name'), + 'path' => 'name' + ], + [ + 'key' => __('Url'), + 'path' => 'url' + ], + [ + 'key' => __('Description'), + 'path' => 'description' + ], + [ + 'key' => __('Owner'), + 'path' => 'organisation.name' + ] + ], + 'metaFields' => empty($metaFields) ? [] : $metaFields, + 'children' => [ + [ + 'url' => '/Broods/previewIndex/{{0}}/organisations', + 'url_params' => ['id'], + 'title' => __('Organisations') + ], + [ + 'url' => '/Broods/previewIndex/{{0}}/individuals', + 'url_params' => ['id'], + 'title' => __('Individuals') + ], + [ + 'url' => '/Broods/previewIndex/{{0}}/sharingGroups', + 'url_params' => ['id'], + 'title' => __('Sharing groups') + ] + ] + ] +);