mirror of https://github.com/MISP/MISP
new: [sharing group blueprints]
- create a rule based blueprint that is used to create and update a sharing group - nest sharing groups - filter organisations by metadata fields - nested via boolean operators - CLI exposed - API exposed - Lightweight ownership model (only blueprint owner can see and edit the blueprint)pull/8183/head
parent
0c4f225e71
commit
639a4929e3
|
@ -8,7 +8,7 @@ App::uses('ProcessTool', 'Tools');
|
|||
*/
|
||||
class AdminShell extends AppShell
|
||||
{
|
||||
public $uses = array('Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation', 'AdminSetting', 'Galaxy', 'Taxonomy', 'Warninglist', 'Noticelist', 'ObjectTemplate', 'Bruteforce', 'Role', 'Feed');
|
||||
public $uses = array('Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation', 'AdminSetting', 'Galaxy', 'Taxonomy', 'Warninglist', 'Noticelist', 'ObjectTemplate', 'Bruteforce', 'Role', 'Feed', 'SharingGroupBlueprint');
|
||||
|
||||
public function getOptionParser()
|
||||
{
|
||||
|
@ -1142,4 +1142,37 @@ class AdminShell extends AppShell
|
|||
$this->out($setting['setting'] . ': ' . $setting['errorMessage']);
|
||||
}
|
||||
}
|
||||
|
||||
public function executeSGBlueprint()
|
||||
{
|
||||
$id = false;
|
||||
$target = 'all';
|
||||
if (!empty($this->args[0])) {
|
||||
$target = trim($this->args[0]);
|
||||
}
|
||||
if (!is_numeric($target) && !in_array($target, ['all', 'attached', 'deteached'])) {
|
||||
$this->error(__('Invalid target. Either pass a blueprint ID or one of the following filters: all, attached, detached.'));
|
||||
}
|
||||
$conditions = [];
|
||||
if (is_numeric($target)) {
|
||||
$conditions['SharingGroupBlueprint']['id'] = $target;
|
||||
} else if ($target === 'attached') {
|
||||
$conditions['SharingGroupBlueprint']['sharing_group_id >'] = 0;
|
||||
} else if ($target === 'detached') {
|
||||
$conditions['SharingGroupBlueprint']['sharing_group_id'] = 0;
|
||||
}
|
||||
$sharingGroupBlueprints = $this->SharingGroupBlueprint->find('all', ['conditions' => $conditions, 'recursive' => 0]);
|
||||
if (empty($sharingGroupBlueprints)) {
|
||||
$this->error(__('No valid blueprints found.'));
|
||||
}
|
||||
$stats = $this->SharingGroupBlueprint->execute($sharingGroupBlueprints);
|
||||
$message = __(
|
||||
'Done, %s sharing group blueprint(s) matched. Sharing group changes: Created: %s. Updated: %s. Failed to create: %s.',
|
||||
count($sharingGroupBlueprints),
|
||||
$stats['created'],
|
||||
$stats['changed'],
|
||||
$stats['failed']
|
||||
);
|
||||
$this->out($message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class SharingGroupBlueprintsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
||||
public function beforeFilter()
|
||||
{
|
||||
parent::beforeFilter();
|
||||
}
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999
|
||||
);
|
||||
|
||||
public function index()
|
||||
{
|
||||
$params = [
|
||||
'filters' => ['name', 'uuid'],
|
||||
'quickFilters' => ['name']
|
||||
];
|
||||
$this->CRUD->index($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'indexMG'));
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$currentUser = $this->Auth->user();
|
||||
$params = [
|
||||
'beforeSave' => function($data) use ($currentUser) {
|
||||
$data['SharingGroupBlueprint']['uuid'] = CakeText::uuid();
|
||||
$data['SharingGroupBlueprint']['user_id'] = $currentUser['id'];
|
||||
$data['SharingGroupBlueprint']['org_id'] = $currentUser['org_id'];
|
||||
return $data;
|
||||
}
|
||||
];
|
||||
$this->CRUD->add($params);
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'addMG'));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'editMG'));
|
||||
$this->set('id', $id);
|
||||
$params = [
|
||||
'fields' => ['rules']
|
||||
];
|
||||
$this->CRUD->edit($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$this->CRUD->delete($id);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$this->set('menuData', ['menuList' => 'sync', 'menuItem' => 'view_cerebrate']);
|
||||
$this->CRUD->view($id, ['contain' => ['Organisation.name', 'Organisation.uuid', 'Organisation.id', 'SharingGroup.id', 'SharingGroup.name']]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('id', $id);
|
||||
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'viewMG'));
|
||||
}
|
||||
|
||||
public function viewOrgs($id)
|
||||
{
|
||||
$conditions = ['SharingGroupBlueprint.id' => $id];
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$conditions['SharingGroupBlueprint.org_id'] = $this->Auth->user('org_id');
|
||||
}
|
||||
$sharingGroupBlueprint = $this->SharingGroupBlueprint->find('first', ['conditions' => $conditions]);
|
||||
if (empty($sharingGroupBlueprint)) {
|
||||
throw new NotFoundException(__('Invalid Sharing Group Blueprint'));
|
||||
}
|
||||
// we create a fake user to restrict the visible sharing groups to the creator of the SharingGroupBlueprint, in case an admin wants to update it
|
||||
$fake_user = [
|
||||
'Role' => [
|
||||
'perm_site_admin' => false
|
||||
],
|
||||
'org_id' => $sharingGroupBlueprint['SharingGroupBlueprint']['org_id'],
|
||||
'id' => 1
|
||||
];
|
||||
$temp = $this->SharingGroupBlueprint->evaluateSharingGroupBlueprint($sharingGroupBlueprint, $fake_user);
|
||||
$orgs = $this->SharingGroupBlueprint->SharingGroup->Organisation->find('all', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['id', 'uuid', 'name', 'sector', 'type', 'nationality'],
|
||||
'conditions' => ['id' => $temp['orgs']]
|
||||
]);
|
||||
$this->set('data', $orgs);
|
||||
$this->set('menuData', array('menuList' => 'SharingGroupBlueprints', 'menuItem' => 'viewOrgs'));
|
||||
}
|
||||
|
||||
public function execute($id = false)
|
||||
{
|
||||
$conditions = [];
|
||||
if (!empty($id)) {
|
||||
$conditions['SharingGroupBlueprint.id'] = $id;
|
||||
}
|
||||
if (!$this->Auth->user('Role')['perm_admin']) {
|
||||
$conditions['SharingGroupBlueprint.org_id'] = $this->Auth->user('org_id');
|
||||
}
|
||||
$sharingGroupBlueprints = $this->SharingGroupBlueprint->find('all', ['conditions' => $conditions, 'recursive' => 0]);
|
||||
if (empty($sharingGroupBlueprints)) {
|
||||
throw new NotFoundException(__('No valid blueprints found.'));
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$stats = $this->SharingGroupBlueprint->execute($sharingGroupBlueprints);
|
||||
$message = __(
|
||||
'Done, %s sharing group blueprint(s) matched. Sharing group changes: Created: %s. Updated: %s. Failed to create: %s.',
|
||||
count($sharingGroupBlueprints),
|
||||
$stats['created'],
|
||||
$stats['changed'],
|
||||
$stats['failed']
|
||||
);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
if ($stats['changed'] || $stats['created']) {
|
||||
return $this->RestResponse->saveSuccessResponse('sharingGroupBlueprints', 'execute', $id, false, $message);
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('sharingGroupBlueprints', 'execute', $id, $message, $this->response->type());
|
||||
}
|
||||
} else {
|
||||
$status = 'success';
|
||||
if ($stats['failed']) {
|
||||
$status = 'error';
|
||||
if ($stats['created'] || $stats['changed']) {
|
||||
$status = 'info';
|
||||
}
|
||||
}
|
||||
$this->Flash->{$status}($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$this->set('id', empty($id) ? $id : 'all');
|
||||
$this->set('title', __('Execute Sharing Group Blueprint'));
|
||||
$this->set('question', __('Are you sure you want to (re)create a sharing group based on the Sharing Group Blueprint?'));
|
||||
$this->set('actionName', __('Execute'));
|
||||
$this->layout = 'ajax';
|
||||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
|
||||
public function detach($id)
|
||||
{
|
||||
$conditions = [];
|
||||
if (empty($id)) {
|
||||
throw new MethodNotAllowedException(__('No ID specified.'));
|
||||
}
|
||||
$conditions['SharingGroupBlueprint.id'] = $id;
|
||||
if (!$this->Auth->user('Role')['perm_admin']) {
|
||||
$conditions['SharingGroupBlueprint.org_id'] = $this->Auth->user('org_id');
|
||||
}
|
||||
$sharingGroupBlueprint = $this->SharingGroupBlueprint->find('first', ['conditions' => $conditions, 'recursive' => -1]);
|
||||
if (empty($sharingGroupBlueprint)) {
|
||||
throw new NotFoundException(__('Invalid Sharing Group Blueprint'));
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$sharingGroupBlueprint['SharingGroupBlueprint']['sharing_group_id'] = 0;
|
||||
$result = $this->SharingGroupBlueprint->save($sharingGroupBlueprint);
|
||||
$message = $result ? __('Sharing group detached.') : __('Could not detach sharing group.');
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
if ($result) {
|
||||
return $this->RestResponse->saveSuccessResponse('sharingGroupBlueprints', 'detach', $id, false, $message);
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('sharingGroupBlueprints', 'detach', $id, $message, $this->response->type());
|
||||
}
|
||||
} else {
|
||||
$this->Flash->{$result ? 'success' : 'error'}($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$this->set('id', $id);
|
||||
$this->set('title', __('Detach Sharing Group Blueprint'));
|
||||
$this->set('question', __('Are you sure you want to detach the associated sharing group from this Sharing Group Blueprint? This action is irreversible.'));
|
||||
$this->set('actionName', __('Detach'));
|
||||
$this->layout = 'ajax';
|
||||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -85,7 +85,7 @@ class AppModel extends Model
|
|||
57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
|
||||
63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false,
|
||||
69 => false, 70 => false, 71 => true, 72 => true, 73 => false, 74 => false,
|
||||
75 => false, 76 => true, 77 => false, 78 => false, 79 => false,
|
||||
75 => false, 76 => true, 77 => false, 78 => false, 79 => false, 80 => false
|
||||
);
|
||||
|
||||
public $advanced_updates_description = array(
|
||||
|
@ -1614,6 +1614,23 @@ class AppModel extends Model
|
|||
$sqlArray[] = "ALTER TABLE `users` ADD `sub` varchar(255) NULL DEFAULT NULL;";
|
||||
$sqlArray[] = "ALTER TABLE `users` ADD UNIQUE INDEX `sub` (`sub`);";
|
||||
break;
|
||||
case 80:
|
||||
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `sharing_group_blueprints` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(40) COLLATE utf8_bin NOT NULL ,
|
||||
`name` varchar(191) NOT NULL,
|
||||
`timestamp` int(11) NOT NULL DEFAULT 0,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`org_id` int(11) NOT NULL,
|
||||
`sharing_group_id` int(11),
|
||||
`rules` text,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `uuid` (`uuid`),
|
||||
INDEX `name` (`name`),
|
||||
INDEX `org_id` (`org_id`),
|
||||
INDEX `sharing_group_id` (`sharing_group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
break;
|
||||
case 'fixNonEmptySharingGroupID':
|
||||
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
|
|
|
@ -38,6 +38,7 @@ class Log extends AppModel
|
|||
'enable',
|
||||
'enrichment',
|
||||
'error',
|
||||
'execute_blueprint',
|
||||
'export',
|
||||
'fetchEvent',
|
||||
'file_upload',
|
||||
|
|
|
@ -7268,6 +7268,7 @@ class Server extends AppModel
|
|||
'Enqueue push' => 'MISP/app/Console/cake Server enqueuePush [timestamp] [task_id] [user_id]',
|
||||
'Enqueue feed fetch' => 'MISP/app/Console/cake Server enqueueFeedFetch [timestamp] [user_id] [task_id]',
|
||||
'Enqueue feed cache' => 'MISP/app/Console/cake Server enqueueFeedCache [timestamp] [user_id] [task_id]',
|
||||
'Update sharing groups based on blueprints' => 'MISP/app/Console/cake Server executeSGBlueprint [blueprint_id|all|attached|detached]'
|
||||
),
|
||||
'description' => __('If you would like to automate tasks such as caching feeds or pulling from server instances, you can do it using the following command line tools. Simply execute the given commands via the command line / create cron jobs easily out of them.'),
|
||||
'header' => __('Automating certain console tasks')
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('EncryptedValue', 'Tools');
|
||||
|
||||
class SharingGroupBlueprint extends AppModel
|
||||
{
|
||||
public $actsAs = [
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => [
|
||||
'roleModel' => 'Role',
|
||||
'roleKey' => 'role_id',
|
||||
'change' => 'full'
|
||||
],
|
||||
'Containable'
|
||||
];
|
||||
|
||||
public $belongsTo = array(
|
||||
'SharingGroup',
|
||||
'Organisation' => array(
|
||||
'className' => 'Organisation',
|
||||
'foreignKey' => 'org_id'
|
||||
)
|
||||
);
|
||||
|
||||
public $validFilters = [
|
||||
'org' => [
|
||||
'org_id' => 'id',
|
||||
'org_uuid' => 'uuid',
|
||||
'org_name' => 'name',
|
||||
'org_nationality' => 'nationality',
|
||||
'org_sector' => 'sector',
|
||||
'org_type' => 'type'
|
||||
],
|
||||
'sharing_group' => [
|
||||
'sharing_group_id' => 'id',
|
||||
'sharing_group_uuid' => 'uuid'
|
||||
]
|
||||
];
|
||||
|
||||
public $operands = [
|
||||
'OR',
|
||||
'AND',
|
||||
'NOT'
|
||||
];
|
||||
|
||||
public function beforeSave($options = array())
|
||||
{
|
||||
$this->data['SharingGroupBlueprint']['timestamp'] = time();
|
||||
$this->data['SharingGroupBlueprint']['rules'] = json_decode($this->data['SharingGroupBlueprint']['rules']);
|
||||
$this->data['SharingGroupBlueprint']['rules'] = json_encode($this->data['SharingGroupBlueprint']['rules']);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
foreach ($results as &$v) {
|
||||
$v['SharingGroupBlueprint']['rules'] = json_encode(json_decode($v['SharingGroupBlueprint']['rules']), JSON_PRETTY_PRINT);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function execute($sharingGroupBlueprints)
|
||||
{
|
||||
$stats = [
|
||||
'changed' => 0,
|
||||
'created' => 0,
|
||||
'failed' => 0
|
||||
];
|
||||
$updated = $failed = 0;
|
||||
foreach ($sharingGroupBlueprints as $sharingGroupBlueprint) {
|
||||
// we create a fake user to restrict the visible sharing groups to the creator of the SharingGroupBlueprint, in case an admin wants to update it
|
||||
$fake_user = [
|
||||
'Role' => [
|
||||
'perm_site_admin' => false
|
||||
],
|
||||
'org_id' => $sharingGroupBlueprint['SharingGroupBlueprint']['org_id'],
|
||||
'id' => 1
|
||||
];
|
||||
$result = $this->updateSharingGroup($sharingGroupBlueprint, $fake_user);
|
||||
foreach (array_keys($stats) as $field) {
|
||||
$stats[$field] += $result[$field];
|
||||
}
|
||||
|
||||
}
|
||||
return $stats;
|
||||
}
|
||||
|
||||
public function updateSharingGroup($sharingGroupBlueprint, $user)
|
||||
{
|
||||
$this->Organisation = ClassRegistry::init('Organisation');
|
||||
$data = $this->evaluateSharingGroupBlueprint($sharingGroupBlueprint, $user);
|
||||
$failed = 0;
|
||||
if (empty($sharingGroupBlueprint['SharingGroupBlueprint']['sharing_group_id'])) {
|
||||
$created = true;
|
||||
$this->SharingGroup->create();
|
||||
$org_uuid = $this->SharingGroup->Organisation->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Organisation.id' => $sharingGroupBlueprint['SharingGroupBlueprint']['org_id']],
|
||||
'fields' => ['Organisation.uuid']
|
||||
]);
|
||||
if (empty($org_uuid)) {
|
||||
throw new MethodNotAllowedException(__('Invalid owner organisation.'));
|
||||
}
|
||||
$org_uuid = $org_uuid['Organisation']['uuid'];
|
||||
$sg = [
|
||||
'name' => $sharingGroupBlueprint['SharingGroupBlueprint']['name'],
|
||||
'description' => __('Generated based on Sharing Group Blueprint rules'),
|
||||
'org_id' => $user['org_id'],
|
||||
'organisation_uuid' => $org_uuid,
|
||||
'releasability' => __('Generated based on Sharing Group Blueprint rules'),
|
||||
'local' => 1,
|
||||
'roaming' => 1
|
||||
];
|
||||
if ($this->SharingGroup->save($sg)) {
|
||||
$id = $this->SharingGroup->id;
|
||||
$sharingGroupBlueprint['SharingGroupBlueprint']['sharing_group_id'] = $id;
|
||||
$existingOrgs = [];
|
||||
$this->save($sharingGroupBlueprint);
|
||||
} else {
|
||||
$failed++;
|
||||
}
|
||||
|
||||
} else {
|
||||
$created = false;
|
||||
$sg = $this->SharingGroup->find('first', [
|
||||
'recursive' => -1,
|
||||
'contain' => ['SharingGroupOrg'],
|
||||
'conditions' => ['SharingGroup.id' => $sharingGroupBlueprint['SharingGroupBlueprint']['sharing_group_id']]
|
||||
]);
|
||||
$existingOrgs = [];
|
||||
foreach ($sg['SharingGroupOrg'] as $sgo) {
|
||||
$existingOrgs[] = $sgo['org_id'];
|
||||
}
|
||||
$existingOrgs = array_unique($existingOrgs);
|
||||
$id = $sg['SharingGroup']['id'];
|
||||
}
|
||||
return [
|
||||
'id' => $id,
|
||||
'changed' => !$created && $this->__handleSharingGroupOrgs($existingOrgs, $data['orgs'], $id),
|
||||
'created' => $created,
|
||||
'failed' => $failed
|
||||
];
|
||||
}
|
||||
|
||||
private function __handleSharingGroupOrgs($existingOrgs, $newOrgs, $id)
|
||||
{
|
||||
$added = 0;
|
||||
$removed = 0;
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
foreach ($existingOrgs as $existingOrg) {
|
||||
if (!in_array($existingOrg, $newOrgs)) {
|
||||
$this->SharingGroup->SharingGroupOrg->deleteAll([
|
||||
'sharing_group_id' => $id,
|
||||
'org_id' => $existingOrg
|
||||
], false);
|
||||
$removed++;
|
||||
}
|
||||
}
|
||||
foreach ($newOrgs as $newOrg) {
|
||||
if (!in_array($newOrg, $existingOrgs)) {
|
||||
$sgo = [
|
||||
'sharing_group_id' => $id,
|
||||
'org_id' => $newOrg,
|
||||
'extend' => false
|
||||
];
|
||||
$this->SharingGroup->SharingGroupOrg->create();
|
||||
$this->SharingGroup->SharingGroupOrg->save($sgo);
|
||||
$added++;
|
||||
}
|
||||
}
|
||||
if ($added || $removed) {
|
||||
$this->Log->create();
|
||||
$entry = array(
|
||||
'org' => 'SYSTEM',
|
||||
'model' => 'SharingGroup',
|
||||
'model_id' => $id,
|
||||
'email' => 'SYSTEM',
|
||||
'action' => 'execute_blueprint',
|
||||
'user_id' => 0,
|
||||
'title' => 'Updated the sharing group.',
|
||||
'change' => __('Updated sharing group. Added %s and removed %s organisations', $added, $removed)
|
||||
);
|
||||
$this->Log->save($entry);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Walking on water and developing software from a specification are easy if both are frozen - Edward V Berard
|
||||
public function evaluateSharingGroupBlueprint($sharingGroupBlueprint, $user)
|
||||
{
|
||||
$data = [];
|
||||
$rules = json_decode($sharingGroupBlueprint['SharingGroupBlueprint']['rules'], true);
|
||||
$data = $this->__recursiveEvaluate($user, $rules, 'OR');
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function __recursiveEvaluate($user, $rules, $operand)
|
||||
{
|
||||
if (!empty($rules)) {
|
||||
$data = [];
|
||||
foreach ($rules as $key => $value) {
|
||||
if (in_array($key, $this->operands)) {
|
||||
if ($operand === 'NOT') {
|
||||
throw new MethodNotAllwedException(__('Boolean branches within a NOT branch are not supported.'));
|
||||
}
|
||||
$temp = $this->__recursiveEvaluate($user, $rules[$key], $key);
|
||||
} else {
|
||||
$negation = $operand === 'NOT';
|
||||
$temp = $this->__evaluateLeaf($user, $key, $value, $negation);
|
||||
}
|
||||
if ($operand === 'OR') {
|
||||
if (!isset($data['orgs'])) {
|
||||
$data['orgs'] = [];
|
||||
}
|
||||
$data['orgs'] = array_merge(
|
||||
$data['orgs'],
|
||||
isset($temp['orgs']) ? $temp['orgs'] : []
|
||||
);
|
||||
} else if ($operand === 'AND' || $operand === 'NOT') {
|
||||
if (!isset($data['orgs'])) {
|
||||
$data['orgs'] = $temp['orgs'];
|
||||
} else {
|
||||
$data['orgs'] = array_intersect($data['orgs'], $temp['orgs']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function __evaluateLeaf($user, $key, $value, $negation = false)
|
||||
{
|
||||
if (substr($key, 0, strlen('org')) === 'org') {
|
||||
return $this->__evaluateOrgLeaf(
|
||||
$user,
|
||||
substr($key, (strlen('org_'))),
|
||||
$value,
|
||||
$negation
|
||||
);
|
||||
} else if (substr($key, 0, strlen('sharing_group')) === 'sharing_group') {
|
||||
return $this->__evaluateSGLeaf(
|
||||
$user,
|
||||
substr($key, (strlen('sharing_group_'))),
|
||||
$value,
|
||||
$negation
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private function __evaluateOrgLeaf($user, $key, $value, $negation)
|
||||
{
|
||||
if (in_array($key, $this->validFilters['org'])) {
|
||||
$conditions = [$key => $value];
|
||||
if ($negation) {
|
||||
$conditions = ['NOT' => $conditions];
|
||||
}
|
||||
$orgs = $this->SharingGroup->Organisation->find('list', [
|
||||
'fields' => ['id', 'id'],
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions
|
||||
]);
|
||||
$orgs = array_values($orgs);
|
||||
if (empty($orgs)) {
|
||||
$orgs = [-1];
|
||||
}
|
||||
return [
|
||||
'orgs' => $orgs
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private function __evaluateSGLeaf($user, $key, $value, $negation)
|
||||
{
|
||||
$orgs = [];
|
||||
if (in_array($key, $this->validFilters['sharing_group'])) {
|
||||
$conditions = [$key => $value];
|
||||
if ($negation) {
|
||||
$conditions = ['NOT' => $conditions];
|
||||
}
|
||||
$sgs = $this->SharingGroup->find('all', [
|
||||
'fields' => ['id', 'uuid', 'name', 'org_id'],
|
||||
'contain' => ['SharingGroupOrg.org_id'],
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions
|
||||
]);
|
||||
foreach ($sgs as $sg) {
|
||||
if ($this->SharingGroup->checkIfAuthorised($user, $sg['SharingGroup']['id'])) {
|
||||
$orgs[$sg['SharingGroup']['org_id']] = true;
|
||||
foreach ($sg['SharingGroupOrg'] as $sgo) {
|
||||
$orgs[$sgo['org_id']] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$orgs = array_keys($orgs);
|
||||
if (empty($orgs)) {
|
||||
$orgs = [-1];
|
||||
}
|
||||
return [
|
||||
'orgs' => $orgs
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -672,6 +672,25 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
|
|||
'text' => __('View Sharing Group')
|
||||
));
|
||||
}
|
||||
if ($menuItem === 'editMG' || ($menuItem == 'viewMG' && $isAclSharingGroup)) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'editMG',
|
||||
'url' => $baseurl . '/sharing_group_blueprints/edit/' . h($id),
|
||||
'text' => __('Edit Sharing Group Blueprint')
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'viewMG',
|
||||
'url' => $baseurl . '/sharing_group_blueprints/view/' . h($id),
|
||||
'text' => __('View Sharing Group Blueprint')
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'text' => __('Execute Sharing Group Blueprint'),
|
||||
'onClick' => array(
|
||||
'function' => 'openGenericModal',
|
||||
'params' => array($baseurl . '/sharing_group_blueprints/execute/' . h($id))
|
||||
),
|
||||
));
|
||||
}
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'indexSG',
|
||||
'url' => $baseurl . '/sharing_groups/index',
|
||||
|
@ -683,6 +702,16 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
|
|||
'url' => $baseurl . '/sharing_groups/add',
|
||||
'text' => __('Add Sharing Group')
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'indexMG',
|
||||
'url' => $baseurl . '/sharing_group_blueprints/index',
|
||||
'text' => __('List Sharing Group Blueprints')
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'addMG',
|
||||
'url' => $baseurl . '/sharing_group_blueprints/add',
|
||||
'text' => __('Add Sharing Group Blueprint')
|
||||
));
|
||||
}
|
||||
echo $divider;
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
|
|
|
@ -220,6 +220,16 @@
|
|||
'url' => $baseurl . '/sharing_groups/add',
|
||||
'requirement' => $isAclSharingGroup
|
||||
),
|
||||
array(
|
||||
'text' => __('List Sharing Groups Blueprints'),
|
||||
'url' => $baseurl . '/sharing_group_blueprints/index',
|
||||
'requirement' => $isAclSharingGroup
|
||||
),
|
||||
array(
|
||||
'text' => __('Add Sharing Group Blueprint'),
|
||||
'url' => $baseurl . '/sharing_group_blueprints/add',
|
||||
'requirement' => $isAclSharingGroup
|
||||
),
|
||||
array(
|
||||
'type' => 'separator'
|
||||
),
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
$modelForForm = 'SharingGroupBlueprints';
|
||||
$edit = $this->request->params['action'] === 'edit' ? true : false;
|
||||
$fields = [
|
||||
[
|
||||
'field' => 'name',
|
||||
'class' => 'span6'
|
||||
],
|
||||
[
|
||||
'field' => 'rules',
|
||||
'type' => 'textarea'
|
||||
]
|
||||
];
|
||||
$description = sprintf(
|
||||
'%s<br />%s<br /><br />%s<br />%s',
|
||||
__('Create a sharing group blueprint, which can be used to generate a sharing rule based on the nested rules described.'),
|
||||
__('Simply create a JSON dictionary using a combination of filters and boolean operators.'),
|
||||
'<span class="bold">Filters</span>: org_id, org_type, org_uuid, org_name, org_sector, org_nationality, sharing_group_id, , sharing_group_uuid',
|
||||
'<span class="bold">Boolean operators</span>: OR, AND, NOT',
|
||||
|
||||
);
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'description' => $description,
|
||||
'model' => 'SharingGroupBlueprint',
|
||||
'title' => $edit ? __('Edit SharingGroupBlueprint') : __('Add SharingGroupBlueprint'),
|
||||
'fields' => $fields,
|
||||
'submit' => [
|
||||
'action' => $this->request->params['action'],
|
||||
'ajaxSubmit' => 'submitGenericFormInPlace();'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
if (!$ajax) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
|
||||
}
|
||||
|
||||
echo $this->element('genericElements/assetLoader', array(
|
||||
'js' => array(
|
||||
'codemirror/codemirror',
|
||||
'codemirror/modes/javascript',
|
||||
'codemirror/addons/closebrackets',
|
||||
'codemirror/addons/lint',
|
||||
'codemirror/addons/jsonlint',
|
||||
'codemirror/addons/json-lint',
|
||||
),
|
||||
'css' => array(
|
||||
'codemirror',
|
||||
'codemirror/show-hint',
|
||||
'codemirror/lint',
|
||||
)
|
||||
));
|
||||
?>
|
||||
|
||||
<script>
|
||||
var cm;
|
||||
setupCodeMirror()
|
||||
|
||||
function setupCodeMirror() {
|
||||
var cmOptions = {
|
||||
mode: "application/json",
|
||||
theme:'default',
|
||||
gutters: ["CodeMirror-lint-markers"],
|
||||
lint: true,
|
||||
lineNumbers: true,
|
||||
indentUnit: 4,
|
||||
showCursorWhenSelecting: true,
|
||||
lineWrapping: true,
|
||||
autoCloseBrackets: true
|
||||
}
|
||||
cm = CodeMirror.fromTextArea(document.getElementById('SharingGroupBlueprintRules'), cmOptions);
|
||||
cm.on("keyup", function(cm, event) {
|
||||
$('#urlParams').val(cm.getValue())
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div .CodeMirror {
|
||||
width: 500px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/IndexTable/scaffold', [
|
||||
'scaffold_data' => [
|
||||
'data' => [
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'pull' => 'right',
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
'data' => [
|
||||
'type' => 'simple',
|
||||
'text' => __('Add SharingGroupBlueprint'),
|
||||
'class' => 'btn btn-primary',
|
||||
'url' => sprintf(
|
||||
'%s/SharingGroupBlueprints/add',
|
||||
$baseurl
|
||||
)
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Filter'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'quickFilter'
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => __('Id'),
|
||||
'sort' => 'SharingGroupBlueprint.id',
|
||||
'data_path' => 'SharingGroupBlueprint.id'
|
||||
],
|
||||
[
|
||||
'name' => __('Owner organisation'),
|
||||
'sort' => 'Organisation',
|
||||
'data_path' => 'Organisation',
|
||||
'element' => 'org'
|
||||
],
|
||||
[
|
||||
'name' => __('Name'),
|
||||
'sort' => 'SharingGroupBlueprint.name',
|
||||
'data_path' => 'SharingGroupBlueprint.name'
|
||||
],
|
||||
[
|
||||
'name' => __('SharingGroup'),
|
||||
'sort' => 'SharingGroupBlueprint.sharing_group_id',
|
||||
'data_path' => 'SharingGroupBlueprint.sharing_group_id',
|
||||
'element' => 'custom',
|
||||
'function' => function ($row) use ($baseurl) {
|
||||
if (!empty($row['SharingGroupBlueprint']['sharing_group_id'])) {
|
||||
if (!empty($row['SharingGroup'])) {
|
||||
echo sprintf(
|
||||
'<a href="%s/sharingGroups/view/%s" title="%s">#%s: %s</a> %s',
|
||||
$baseurl,
|
||||
h($row['SharingGroup']['id']),
|
||||
h($row['SharingGroup']['releasability']),
|
||||
h($row['SharingGroup']['id']),
|
||||
h($row['SharingGroup']['name']),
|
||||
sprintf(
|
||||
'<a href="#" class="black fas fa-trash" onClick="openGenericModal(\'%s/sharing_group_blueprints/detach/%s\');"></a>',
|
||||
$baseurl,
|
||||
h($row['SharingGroupBlueprint']['id'])
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
echo ' ';
|
||||
}
|
||||
},
|
||||
],
|
||||
[
|
||||
'name' => __('Rules'),
|
||||
'sort' => 'SharingGroupBlueprint.rules',
|
||||
'data_path' => 'SharingGroupBlueprint.rules',
|
||||
'element' => 'json'
|
||||
]
|
||||
],
|
||||
'title' => empty($ajax) ? __('Sharing Group Blueprints') : false,
|
||||
'description' => empty($ajax) ? __('Sharing Group Blueprints are blueprints for the creation of sharing groups') : false,
|
||||
'actions' => [
|
||||
[
|
||||
'url' => $baseurl . '/SharingGroupBlueprints/view',
|
||||
'url_params_data_paths' => ['SharingGroupBlueprint.id'],
|
||||
'icon' => 'eye'
|
||||
],
|
||||
[
|
||||
'url' => $baseurl . '/SharingGroupBlueprints/edit',
|
||||
'url_params_data_paths' => ['SharingGroupBlueprint.id'],
|
||||
'icon' => 'edit'
|
||||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/SharingGroupBlueprints/execute/[onclick_params_data_path]\');',
|
||||
$baseurl
|
||||
),
|
||||
'onclick_params_data_path' => 'SharingGroupBlueprint.id',
|
||||
'icon' => 'recycle',
|
||||
'title' => __('(Re)generate sharing group based on blueprint')
|
||||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/SharingGroupBlueprints/delete/[onclick_params_data_path]\');',
|
||||
$baseurl
|
||||
),
|
||||
'onclick_params_data_path' => 'SharingGroupBlueprint.id',
|
||||
'icon' => 'trash'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
?>
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
echo $this->element(
|
||||
'genericElements/SingleViews/single_view',
|
||||
[
|
||||
'title' => 'Sharing Group Blueprint view',
|
||||
'data' => $data,
|
||||
'fields' => [
|
||||
[
|
||||
'key' => __('Id'),
|
||||
'path' => 'SharingGroupBlueprint.id'
|
||||
],
|
||||
[
|
||||
'key' => __('Uuid'),
|
||||
'path' => 'SharingGroupBlueprint.uuid'
|
||||
],
|
||||
[
|
||||
'key' => __('Owner Organisation'),
|
||||
'path' => 'SharingGroupBlueprint.org_id',
|
||||
'pathName' => 'Organisation.name',
|
||||
'type' => 'model',
|
||||
'model' => 'organisations'
|
||||
],
|
||||
[
|
||||
'key' => __('Name'),
|
||||
'path' => 'SharingGroupBlueprint.name'
|
||||
],
|
||||
[
|
||||
'key' => __('Description'),
|
||||
'path' => 'SharingGroupBlueprint.description'
|
||||
],
|
||||
[
|
||||
'key' => __('SharingGroup'),
|
||||
'path' => 'SharingGroupBlueprint.sharing_group_id',
|
||||
'pathName' => 'SharingGroup.name',
|
||||
'type' => 'model',
|
||||
'model' => 'sharing_groups',
|
||||
'error' => __('No Sharing group assigned yet, execute the Sharing Group Blueprint first.')
|
||||
],
|
||||
[
|
||||
'key' => __('Rules'),
|
||||
'path' => 'SharingGroupBlueprint.rules',
|
||||
'type' => 'json'
|
||||
],
|
||||
],
|
||||
'children' => [
|
||||
[
|
||||
'url' => '/SharingGroupBlueprints/viewOrgs/{{0}}/',
|
||||
'url_params' => ['SharingGroupBlueprint.id'],
|
||||
'title' => __('Organisations'),
|
||||
'elementId' => 'preview_orgs_container'
|
||||
]
|
||||
]
|
||||
]
|
||||
);
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
echo sprintf('<div%s>', empty($ajax) ? ' class="index"' : '');
|
||||
echo $this->element('genericElements/IndexTable/index_table', [
|
||||
'data' => [
|
||||
'skip_pagination' => 1,
|
||||
'data' => $data,
|
||||
'fields' => [
|
||||
[
|
||||
'name' => __('Id'),
|
||||
'sort' => 'Organisation.id',
|
||||
'data_path' => 'Organisation.id'
|
||||
],
|
||||
[
|
||||
'name' => __('Uuid'),
|
||||
'sort' => 'Organisation.uuid',
|
||||
'data_path' => 'Organisation.uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('name'),
|
||||
'sort' => 'Organisation.name',
|
||||
'data_path' => 'Organisation.name'
|
||||
],
|
||||
[
|
||||
'name' => __('sector'),
|
||||
'sort' => 'Organisation.sector',
|
||||
'data_path' => 'Organisation.sector'
|
||||
],
|
||||
[
|
||||
'name' => __('type'),
|
||||
'sort' => 'Organisation.type',
|
||||
'data_path' => 'Organisation.type'
|
||||
],
|
||||
[
|
||||
'name' => __('nationality'),
|
||||
'sort' => 'Organisation.nationality',
|
||||
'data_path' => 'Organisation.nationality'
|
||||
]
|
||||
],
|
||||
'title' => false,
|
||||
'description' => __('Organisations that would end up in a sharing group with the current SharingGroupBlueprint blueprint.'),
|
||||
'actions' => [
|
||||
[
|
||||
'url' => $baseurl . '/organisations/view',
|
||||
'url_params_data_paths' => ['Organisation.id'],
|
||||
'icon' => 'eye'
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
echo '</div>';
|
||||
if (empty($ajax)) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
|
||||
}
|
||||
?>
|
Loading…
Reference in New Issue