From a1ffdc7790cfd59b2d0b4cfe136e3b08b39f9092 Mon Sep 17 00:00:00 2001 From: Iglocska Date: Fri, 12 Feb 2016 05:47:06 +0100 Subject: [PATCH] First finished version --- app/Controller/AppController.php | 41 ++---- app/Controller/EventDelegationsController.php | 134 +++++++++++++++--- app/Controller/EventsController.php | 56 ++++++++ app/Controller/UsersController.php | 20 +++ app/Model/AdminSettingModel.php | 7 + app/Model/AppModel.php | 121 ++++++++++++++-- app/Model/Event.php | 60 +++++++- app/Model/EventDelegation.php | 73 ++++++++++ app/Model/Log.php | 4 +- app/Model/Server.php | 9 ++ .../Elements/dashboard/dashboard_events.ctp | 13 ++ .../dashboard/dashboard_notifications.ctp | 14 ++ app/View/Elements/global_menu.ctp | 20 ++- app/View/Elements/side_menu.ctp | 16 ++- .../ajax/accept_delegation.ctp | 22 +++ .../EventDelegations/ajax/delegate_event.ctp | 83 +++++++---- .../ajax/delete_delegation.ctp | 22 +++ app/View/EventDelegations/ajax/view.ctp | 26 ++++ app/View/Events/filter_event_index.ctp | 82 +++++------ app/View/Events/index.ctp | 5 +- app/View/Events/view.ctp | 38 ++--- app/View/Users/dashboard.ctp | 18 +++ app/webroot/css/main.css | 78 +++++++++- app/webroot/js/ajaxification.js | 16 +++ 24 files changed, 805 insertions(+), 173 deletions(-) create mode 100644 app/Model/AdminSettingModel.php create mode 100644 app/View/Elements/dashboard/dashboard_events.ctp create mode 100644 app/View/Elements/dashboard/dashboard_notifications.ctp create mode 100644 app/View/EventDelegations/ajax/accept_delegation.ctp create mode 100644 app/View/EventDelegations/ajax/delete_delegation.ctp create mode 100644 app/View/EventDelegations/ajax/view.ctp create mode 100644 app/View/Users/dashboard.ctp diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 6dad12d8e..6a9b068f8 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -278,13 +278,22 @@ class AppController extends Controller { } $this->debugMode = 'debugOff'; if (Configure::read('debug') > 1) $this->debugMode = 'debugOn'; - + // update script + $this->{$this->modelClass}->runUpdates(); + $this->set('loggedInUserName', $this->__convertEmailToName($this->Auth->user('email'))); $this->set('debugMode', $this->debugMode); - $proposalCount = $this->_getProposalCount(); - $this->set('proposalCount', $proposalCount[0]); - $this->set('proposalEventCount', $proposalCount[1]); + $notifications = $this->{$this->modelClass}->populateNotifications($this->Auth->user()); + $this->set('notifications', $notifications); $this->set('mispVersion', $this->mispVersion); } + + private function __convertEmailToName($email) { + $name = explode('@', $email); + $name = explode('.', $name[0]); + foreach ($name as &$temp) $temp = ucfirst($temp); + $name = implode(' ', $name); + return $name; + } public function blackhole($type) { if ($type === 'csrf') throw new BadRequestException(__d('cake_dev', $type)); @@ -314,26 +323,6 @@ class AppController extends Controller { } return false; } - - private function _getProposalCount() { - $this->loadModel('ShadowAttribute'); - $this->ShadowAttribute->recursive = -1; - $shadowAttributes = $this->ShadowAttribute->find('all', array( - 'recursive' => -1, - 'fields' => array('event_id', 'event_org_id'), - 'conditions' => array( - 'ShadowAttribute.event_org_id' => $this->Auth->user('org_id'), - 'ShadowAttribute.deleted' => 0, - ))); - $results = array(); - $eventIds = array(); - $results[0] = count($shadowAttributes); - foreach ($shadowAttributes as $sa) { - if (!in_array($sa['ShadowAttribute']['event_id'], $eventIds)) $eventIds[] = $sa['ShadowAttribute']['event_id']; - } - $results[1] = count($eventIds); - return $results; - } /** * Convert an array to the same array but with the values also as index instead of an interface_exists @@ -525,8 +514,4 @@ class AppController extends Controller { $this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration')); } } - - public function test() { - $this->{$this->modelClass}->runUpdates(); - } } \ No newline at end of file diff --git a/app/Controller/EventDelegationsController.php b/app/Controller/EventDelegationsController.php index fe37e8a1b..95077831d 100644 --- a/app/Controller/EventDelegationsController.php +++ b/app/Controller/EventDelegationsController.php @@ -16,35 +16,135 @@ class EventDelegationsController extends AppController { ), ); - public function index() { - } - - public function add() { - - } - - public function edit($id) { - - } - - public function delete($id) { - + public function view($id) { + $delegation = $this->EventDelegation->find('first', array( + 'conditions' => array('EventDelegation.id' => $id), + 'recursive' => -1, + 'contain' => array('Org', 'Event', 'RequesterOrg', 'SharingGroup'), + )); + if (empty($delegation) || (!$this->_isSiteAdmin() && $this->Auth->user('org_id') != $delegation['EventDelegation']['org_id'] && $this->Auth->user('org_id') != $delegation['EventDelegation']['requester_org_id'])) throw new MethodNotAllowedException('You are not authorised to do that.'); + $delegation['requested_distribution_level'] = $delegation['EventDelegation']['distribution'] == -1 ? false : $this->EventDelegation->Event->distributionLevels[$delegation['EventDelegation']['distribution']]; + $this->set('delegation', $delegation); + $this->render('ajax/view'); } public function delegateEvent($id) { - debug($this->EventDelegation->find('all')); $event = $this->EventDelegation->Event->find('first', array( 'conditions' => array('Event.id' => $id), 'recursive' => -1, 'fields' => array('Event.id', 'Event.orgc_id', 'Event.distribution') )); - if (!$this->_isSiteAdmin() || $this->Auth->user('org_id') !== $event['Event']['orgc_id']) throw new MethodNotAllowedException('You are not authorised to do that.'); + if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') !== $event['Event']['orgc_id']) throw new MethodNotAllowedException('You are not authorised to do that.'); if ($event['Event']['distribution'] != 0) throw new MethodNotAllowedException('Only events with the distribution setting "Your Organisation Only" can be delegated.'); + $existingDelegations = $this->EventDelegation->find('first', array('conditions' => array('event_id' => $id), 'recursive' => -1)); + if (!empty($existingDelegations)) throw new MethodNotAllowedException('This event already has a pending delegation request. Please revoke that before creating a new request.'); if ($this->request->is('Post')) { - + if ($this->request->data['EventDelegation']['distribution'] != 4) $this->request->data['EventDelegation']['sharing_group_id'] = '0'; + $this->request->data['EventDelegation']['event_id'] = $event['Event']['id']; + $this->request->data['EventDelegation']['requester_org_id'] = $this->Auth->user('org_id'); + $this->EventDelegation->create(); + $this->EventDelegation->save($this->request->data['EventDelegation']); + $org = $this->EventDelegation->Event->Org->find('first', array( + 'conditions' => array('id' => $this->request->data['EventDelegation']['requester_org_id']), + 'recursive' => -1, + 'fields' => array('name') + )); + $this->Log = ClassRegistry::init('Log'); + $this->Log->create(); + $this->Log->save(array( + 'org' => $this->Auth->user('Organisation')['name'], + 'model' => 'Event', + 'model_id' => $event['Event']['id'], + 'email' => $this->Auth->user('email'), + 'action' => 'request_delegation', + 'user_id' => $this->Auth->user('id'), + 'title' => 'Requested event delegation', + 'change' => 'Requested the delegation of event ' . $event['Event']['id'] . ' to organisation ' . $org['Org']['name'], + )); + $this->Session->setFlash('Delegation request created.'); + $this->redirect('/events/view/' . $id); } else { - + $orgs = $this->EventDelegation->Event->Org->find('list', array( + 'conditions' => array( + 'Org.id !=' => $this->Auth->user('org_id'), + 'Org.local' => 1, + ), + 'fields' => array('name'), + 'order' => array('lower(name) ASC') + )); + $distribution = $this->EventDelegation->Event->distributionLevels; + $sgs = $this->EventDelegation->Event->SharingGroup->fetchAllAuthorised($this->Auth->User, 'name', true); + if (empty($sgs)) unset($distribution[4]); + $distribution[-1] = 'Recipient decides'; + $this->set('distributionOptions', array('-1' => 'Recipient decides') + $distribution); + $this->set('org', $orgs); + $this->set('sgOptions', $sgs); + $this->set('id', $id); + $this->render('ajax/delegate_event'); } } + public function acceptDelegation($id) { + $delegation = $this->EventDelegation->find('first', array( + 'conditions' => array('EventDelegation.id' => $id), + 'recursive' => -1, + 'contain' => array('Org', 'Event'), + )); + if (empty($delegation) || (!$this->_isSiteAdmin() && $this->Auth->user('org_id') != $delegation['EventDelegation']['org_id'])) throw new MethodNotAllowedException('You are not authorised to do that.'); + if ($this->request->is('post')) { + $this->Log = ClassRegistry::init('Log'); + $this->Log->create(); + $this->Log->save(array( + 'org' => $this->Auth->user('Organisation')['name'], + 'model' => 'Event', + 'model_id' => $delegation['Event']['id'], + 'email' => $this->Auth->user('email'), + 'action' => 'accept_delegation', + 'user_id' => $this->Auth->user('id'), + 'title' => 'Accepted event delegation', + 'change' => 'Starting the transfer of event ' . $delegation['Event']['id'] . ' to organisation ' . $this->Auth->user('Organisation')['name'], + )); + $result = $this->EventDelegation->transferEvent($delegation, $this->Auth->user()); + if ($result) { + $this->Log->create(); + $this->Log->save(array( + 'org' => $this->Auth->user('Organisation')['name'], + 'model' => 'Event', + 'model_id' => 0, + 'email' => $this->Auth->user('email'), + 'action' => 'accept_delegation', + 'user_id' => $this->Auth->user('id'), + 'title' => 'Completed event delegation', + 'change' => 'Event ' . $delegation['Event']['id'] . ' successfully transferred to organisation ' . $this->Auth->user('Organisation')['name'], + )); + $this->Session->setFlash('Event ownership transferred.'); + $this->redirect(array('controller' => 'events', 'action' => 'view', $result)); + } else { + $this->Session->setFlash('Something went wrong and the event could not be transferred.'); + $this->redirect(array('controller' => 'Event', 'action' => 'view', $delegation['EventDelegation']['event_id'])); + } + } else { + $this->set('delegationRequest', $delegation); + $this->render('ajax/accept_delegation'); + } + } + + public function deleteDelegation($id) { + $delegation = $this->EventDelegation->find('first', array( + 'conditions' => array('EventDelegation.id' => $id), + 'recursive' => -1, + 'contain' => array('Org', 'Event'), + )); + if (empty($delegation) || (!$this->_isSiteAdmin() && !in_array($this->Auth->user('org_id'), array($delegation['EventDelegation']['requester_org_id'], $delegation['EventDelegation']['org_id'])))) throw new MethodNotAllowedException('You are not authorised to do that.'); + if ($this->request->is('post')) { + $this->EventDelegation->delete($delegation['EventDelegation']['id']); + $this->Session->setFlash('Delegation request deleted.'); + $this->redirect(array('controller' => 'events', 'action' => 'view', $delegation['EventDelegation']['event_id'])); + } else { + $this->set('delegationRequest', $delegation); + $this->render('ajax/delete_delegation'); + } + } + + } diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 784048afb..57fcf144c 100755 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -683,6 +683,12 @@ class EventsController extends AppController { $this->set($variable, $currentModel->{$variable}); } } + if (Configure::read('MISP.delegation')) { + $this->loadModel('EventDelegation'); + $delegationConditions = array('EventDelegation.event_id' => $event['Event']['id']); + if (!$this->_isSiteAdmin() && $this->userRole['perm_publish']) $delegationConditions['OR'] = array('EventDelegation.org_id' => $this->Auth->user('org_id'), 'EventDelegation.requester_org_id' => $this->Auth->user('org_id')); + $this->set('delegationRequest', $this->EventDelegation->find('first', array('conditions' => $delegationConditions, 'recursive' => -1, 'contain' => array('Org', 'RequesterOrg')))); + } $this->set('contributors', $contributors); $this->set('typeGroups', array_keys($this->Event->Attribute->typeGroupings)); } @@ -3295,4 +3301,54 @@ class EventsController extends AppController { } return false; } + + public function delegation_index() { + $this->loadmodel('EventDelegation'); + $delegatedEvents = $this->EventDelegation->find('list', array( + 'conditions' => array('EventDelegation.org_id' => $this->Auth->user('org_id')), + 'fields' => array('event_id') + )); + $this->Event->contain(array('User.email', 'EventTag' => array('Tag'))); + $tags = $this->Event->EventTag->Tag->find('all', array('recursive' => -1)); + $tagNames = array('None'); + foreach ($tags as $k => $v) { + $tagNames[$v['Tag']['id']] = $v['Tag']['name']; + } + $this->set('tags', $tagNames); + $this->paginate = array( + 'limit' => 60, + 'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page. + 'order' => array( + 'Event.timestamp' => 'DESC' + ), + 'contain' => array( + 'Org' => array('fields' => array('id', 'name')), + 'Orgc' => array('fields' => array('id', 'name')), + 'SharingGroup' => array('fields' => array('id', 'name')), + 'ThreatLevel' => array('fields' => array('ThreatLevel.name')) + + ), + 'conditions' => array('Event.id' => $delegatedEvents), + ); + + $this->set('events', $this->paginate()); + $threat_levels = $this->Event->ThreatLevel->find('all'); + $this->set('threatLevels', Set::combine($threat_levels, '{n}.ThreatLevel.id', '{n}.ThreatLevel.name')); + $this->set('eventDescriptions', $this->Event->fieldDescriptions); + $this->set('analysisLevels', $this->Event->analysisLevels); + $this->set('distributionLevels', $this->Event->distributionLevels); + + $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' sharing Group'); + $this->set('shortDist', $shortDist); + $this->set('ajax', false); + $this->set('simple', true); + $this->Event->contain(array('User.email', 'EventTag' => array('Tag'))); + $tags = $this->Event->EventTag->Tag->find('all', array('recursive' => -1)); + $tagNames = array('None'); + foreach ($tags as $k => $v) { + $tagNames[$v['Tag']['id']] = $v['Tag']['name']; + } + $this->set('tags', $tagNames); + $this->render('index'); + } } diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 84f233f0c..c918a98ef 100755 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -589,6 +589,11 @@ class UsersController extends AppController { public function login() { if ($this->Auth->login()) { $this->__extralog("login"); // TODO Audit, __extralog, check: customLog i.s.o. __extralog, no auth user?: $this->User->customLog('login', $this->Auth->user('id'), array('title' => '','user_id' => $this->Auth->user('id'),'email' => $this->Auth->user('email'),'org' => 'IN2')); + $this->User->Behaviors->disable('SysLogLogable.SysLogLogable'); + $this->User->id = $this->Auth->user('id'); + $this->User->saveField('last_login', $this->Auth->user('current_login')); + $this->User->saveField('current_login', time()); + $this->User->Behaviors->enable('SysLogLogable.SysLogLogable'); // TODO removed the auto redirect for now, due to security concerns - will look more into this // $this->redirect($this->Auth->redirectUrl()); $this->redirect(array('controller' => 'events', 'action' => 'index')); @@ -634,6 +639,9 @@ class UsersController extends AppController { 'type' => 'ADMIN', 'uuid' => $this->User->Organisation->generateUuid(), 'local' => 1, + 'type' => '', + 'sector' => '', + 'nationality' => '' )); $this->User->Organisation->save($org); $org_id = $this->User->Organisation->id; @@ -1075,4 +1083,16 @@ class UsersController extends AppController { $this->layout = false; $this->render('ajax/fetchpgpkey'); } + + public function dashBoard() { + $events = array(); + // the last login in the session is not updated after the login - only in the db, so let's fetch it. + $lastLogin = $this->Auth->user('last_login'); + $this->loadModel('Event'); + $events['changed'] = count($this->Event->fetchEventIds($this->Auth->user(), false, false, false, true, $lastLogin)); + $events['published'] = count($this->Event->fetchEventIds($this->Auth->user(), false, false, false, true, false, $lastLogin)); + $notifications = $this->{$this->modelClass}->populateNotifications($this->Auth->user()); + $this->set('notifications', $notifications); + $this->set('events', $events); + } } diff --git a/app/Model/AdminSettingModel.php b/app/Model/AdminSettingModel.php new file mode 100644 index 000000000..62a88998f --- /dev/null +++ b/app/Model/AdminSettingModel.php @@ -0,0 +1,7 @@ + 'isUnique'); +} \ No newline at end of file diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 25d11c866..2b74a8af7 100755 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -46,11 +46,10 @@ class AppModel extends Model { $this->name = get_class($this); } + // major -> minor -> hotfix -> requires_logout public $db_changes = array( 2 => array( - 4 => array( - 6 => 'enableEventDelegation' - ) + 4 => array(18 => true, 19=>false) ) ); @@ -84,6 +83,10 @@ class AppModel extends Model { $sql = 'DELETE FROM `cake_sessions` WHERE `expires` < ' . time() . ';'; $clean = false; break; + case 'destroyAllSessions': + $sql = 'DELETE FROM `cake_sessions`;'; + $clean = false; + break; case 'addIPLogging': $sql = 'ALTER TABLE `logs` ADD `ip` varchar(45) COLLATE utf8_bin DEFAULT NULL;'; break; @@ -171,13 +174,26 @@ class AppModel extends Model { } } break; - case 'enableEventDelegation': + case 'adminTable': + $sqlArray[] = "CREATE TABLE IF NOT EXISTS `admin_settings` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `setting` varchar(255) COLLATE utf8_bin NOT NULL, + `value` text COLLATE utf8_bin NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + $sqlArray[] = "INSERT INTO `admin_settings` (`setting`, `value`) VALUES ('db_version', '2.4.0')"; + break; + case '2.4.18': + $sqlArray[] = "ALTER TABLE `users` ADD `current_login` INT(11) DEFAULT 0;"; + $sqlArray[] = "ALTER TABLE `users` ADD `last_login` INT(11) DEFAULT 0;"; $sqlArray[] = "CREATE TABLE IF NOT EXISTS `event_delegations` ( `id` int(11) NOT NULL AUTO_INCREMENT, `org_id` int(11) NOT NULL, + `requester_org_id` int(11) NOT NULL, `event_id` int(11) NOT NULL, `message` text, - `distribution` tinyint(4), + `distribution` tinyint(4) NOT NULL DEFAULT '-1', + `sharing_group_id` int(11), PRIMARY KEY (`id`), KEY `org_id` (`org_id`), KEY `event_id` (`event_id`) @@ -286,9 +302,96 @@ class AppModel extends Model { } public function runUpdates() { - $adminTable = $this->query("SHOW TABLES LIKE 'administration';"); - if (empty($adminTable)) $dbVersion = '2.4.0'; - $currentVersion = explode('.', $this->mispVersion); - $dbVersion; + $this->AdminSetting = ClassRegistry::init('AdminSetting'); + $db = ConnectionManager::getDataSource('default'); + $tables = $db->listSources(); + $requiresLogout = false; + // if we don't even have an admin table, time to create it. + if (!in_array('admin_settings', $tables)) { + $this->updateDatabase('adminTable'); + $requiresLogout = true; + } else { + $db_version = $this->AdminSetting->find('first', array('conditions' => array('setting' => 'db_version'))); + $updates = $this->__findUpgrades($db_version['AdminSetting']['value']); + if (!empty($updates)) { + foreach ($updates as $update => $temp) { + $this->updateDatabase($update); + if ($temp) $requiresLogout = true; + $db_version['AdminSetting']['value'] = $update; + $this->AdminSetting->save($db_version); + } + } + } + if ($requiresLogout) { + $this->updateDatabase('destroyAllSessions'); + } + } + private function __findUpgrades($db_version) { + $version = explode('.', $db_version); + $updates = array(); + foreach ($this->db_changes as $major => $rest) { + if ($major < $version[0]) continue; + else if ($major == $version[0]) { + foreach ($rest as $minor => $hotfixes) { + if ($minor < $version[1]) continue; + else if ($minor == $version[1]) { + foreach ($hotfixes as $hotfix => $requiresLogout) if ($hotfix > $version[2]) $updates[$major . '.' . $minor . '.' . $hotfix] = $requiresLogout; + } else { + foreach ($hotfixes as $hotfix => $requiresLogout) $updates[$major . '.' . $minor . '.' . $hotfix] = $requiresLogout; + } + } + } else { + // we'll fill this out when 3.0 comes around + } + } + return $updates; + } + + + public function populateNotifications($user) { + $notifications = array(); + $proposalCount = $this->_getProposalCount($user); + $notifications['total'] = 0; + $notifications['proposalCount'] = $proposalCount[0]; + $notifications['total'] += $proposalCount[0]; + $notifications['proposalEventCount'] = $proposalCount[1]; + if (Configure::read('MISP.delegation')) { + $delegationCount = $this->_getDelegationCount($user); + $notifications['total'] += $delegationCount; + $notifications['delegationCount'] = $delegationCount; + } + return $notifications; + } + + + private function _getProposalCount($user) { + $this->ShadowAttribute = ClassRegistry::init('ShadowAttribute'); + $this->ShadowAttribute->recursive = -1; + $shadowAttributes = $this->ShadowAttribute->find('all', array( + 'recursive' => -1, + 'fields' => array('event_id', 'event_org_id'), + 'conditions' => array( + 'ShadowAttribute.event_org_id' => $user['org_id'], + 'ShadowAttribute.deleted' => 0, + ))); + $results = array(); + $eventIds = array(); + $results[0] = count($shadowAttributes); + foreach ($shadowAttributes as $sa) { + if (!in_array($sa['ShadowAttribute']['event_id'], $eventIds)) $eventIds[] = $sa['ShadowAttribute']['event_id']; + } + $results[1] = count($eventIds); + return $results; + } + + private function _getDelegationCount($user) { + $this->EventDelegation = ClassRegistry::init('EventDelegation'); + $delegations = $this->EventDelegation->find('count', array( + 'recursive' => -1, + 'conditions' => array( + 'EventDelegation.org_id' => $user['org_id'] + ) + )); + return $delegations; } } diff --git a/app/Model/Event.php b/app/Model/Event.php index 2b1bfd0ac..cda61598d 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -982,7 +982,7 @@ class Event extends AppModel { } } - public function fetchEventIds($user, $from = false, $to = false, $last = false, $list = false) { + public function fetchEventIds($user, $from = false, $to = false, $last = false, $list = false, $timestamp = false, $publish_timestamp = false) { $conditions = array(); $isSiteAdmin = $user['Role']['perm_site_admin']; @@ -1013,6 +1013,8 @@ class Event extends AppModel { if ($from) $conditions['AND'][] = array('Event.date >=' => $from); if ($to) $conditions['AND'][] = array('Event.date <=' => $to); if ($last) $conditions['AND'][] = array('Event.publish_timestamp >=' => $last); + if ($timestamp) $conditions['AND'][] = array('Event.timestamp >=' => $timestamp); + if ($publish_timestamp) $conditions['AND'][] = array('Event.publish_timestamp >=' => $publish_timestamp); if ($list) { $params = array( @@ -1055,6 +1057,7 @@ class Event extends AppModel { $isSiteAdmin = $user['Role']['perm_site_admin']; if (isset($options['disableSiteAdmin']) && $options['disableSiteAdmin']) $isSiteAdmin = false; $conditionsAttributes = array(); + //restricting to non-private or same org if the user is not a site-admin. if (!$isSiteAdmin) { $sgids = $this->SharingGroup->fetchAllAuthorised($user); @@ -1078,6 +1081,16 @@ class Event extends AppModel { ) ) ); + // if delegations are enabled, check if there is an event that the current user might see because of the request itself + if (Configure::read('MISP.delegation')) { + $this->EventDelegation = ClassRegistry::init('EventDelegation'); + $delegatedEventIDs = $this->EventDelegation->find('list', array( + 'conditions' => array('EventDelegation.org_id' => $user['org_id']), + 'fields' => array('event_id') + )); + $conditions['AND']['OR']['Event.id'] = $delegatedEventIDs; + } + $conditionsAttributes['AND'][0]['OR'] = array( array('AND' => array( 'Attribute.distribution >' => 0, @@ -1276,7 +1289,8 @@ class Event extends AppModel { } $params = array( 'conditions' => $conditions, //array of conditions - 'fields' => array('Attribute.event_id', 'Attribute.distribution', 'Attribute.category', 'Attribute.type', 'Attribute.value', 'Attribute.comment', 'Attribute.uuid', 'Attribute.to_ids', 'Attribute.timestamp'), + 'fields' => array('Attribute.event_id', 'Attribute.distribution', 'Attribute.category', 'Attribute.type', 'Attribute.value', 'Attribute.comment', 'Attribute.uuid', 'Attribute.to_ids', 'Attribute.timestamp', 'Attribute.id'), + 'sort' => 'Attribute.id ASC' ); if ($includeContext) { @@ -1962,6 +1976,48 @@ class Event extends AppModel { } return $this->validationErrors; } + // format has to be: + // array('Event' => array(), 'Attribute' => array('ShadowAttribute' => array()), 'EventTag' => array(), 'ShadowAttribute' => array()); + public function savePreparedEvent($event) { + unset($event['Event']['id']); + $this->create(); + $this->save($event['Event']); + $event['Event']['id'] = $this->id; + $objects = array('Attribute', 'ShadowAttribute', 'EventTag'); + foreach ($objects as $object_type) { + if (!empty($event[$object_type])) { + $saveMethod = '__savePrepared' . $object_type; + foreach ($event[$object_type] as $object) $this->$saveMethod($object, $event); + } + } + return $event['Event']['id']; + } + + private function __savePreparedAttribute(&$attribute, &$event) { + unset($attribute['id']); + $attribute['event_id'] = $event['Event']['id']; + $this->Attribute->create(); + $this->Attribute->save($attribute); + foreach ($attribute['ShadowAttribute'] as $k => $sa) { + $this->__savePreparedShadowAttribute($sa, $event, $this->Attribute->id); + } + } + + private function __savePreparedShadowAttribute($shadow_attribute, &$event, $old_id = 0) { + unset($shadow_attribute['id']); + $shadow_attribute['event_id'] = $event['Event']['id']; + $shadow_attribute['old_id'] = $old_id; + $this->ShadowAttribute->create(); + $this->ShadowAttribute->save($shadow_attribute); + } + + private function __savePreparedEventTag($event_tag, &$event) { + unset($event_tag['id']); + $event_tag['event_id'] = $event['Event']['id']; + $this->EventTag->create(); + $this->EventTag->save($event_tag); + } + private function __searchUuidInAttributeArray($uuid, &$attr_array) { foreach ($attr_array['Attribute'] as &$attr) { if ($attr['uuid'] == $uuid) return array('Attribute' => $attr); diff --git a/app/Model/EventDelegation.php b/app/Model/EventDelegation.php index 74db67aed..7fdc9bfe3 100644 --- a/app/Model/EventDelegation.php +++ b/app/Model/EventDelegation.php @@ -25,6 +25,12 @@ class EventDelegation extends AppModel { 'Org' => array( 'className' => 'Organisation', ), + 'RequesterOrg' => array( + 'className' => 'Organisation' + ), + 'SharingGroup' => array( + 'className' => 'SharingGroup' + ) ); public function attachTagToEvent($event_id, $tag_id) { @@ -41,4 +47,71 @@ class EventDelegation extends AppModel { } return true; } + + public function transferEvent($delegation, $user) { + $this->Event->Attribute->bindModel( + array( + 'hasMany' => array( + 'ShadowAttribute' => array( + 'className' => 'ShadowAttribute', + 'foreignKey' => 'old_id' + ) + ) + ) + ); + $event = $this->Event->find('first', array( + 'conditions' => array('Event.id' => $delegation['EventDelegation']['event_id']), + 'recursive' => -1, + 'contain' => array( + 'ShadowAttribute' => array( + 'conditions' => array( + 'ShadowAttribute.old_id' => 0, + 'ShadowAttribute.event_id' => $delegation['EventDelegation']['event_id'] + ) + ), + 'EventTag', + 'Attribute' => array( + 'ShadowAttribute' + ) + ), + )); + $event['Event']['user_id'] = $user['id']; + $event['Event']['orgc_id'] = $delegation['EventDelegation']['org_id']; + $event['Event']['org_id'] = $delegation['EventDelegation']['org_id']; + $this->Event->delete($delegation['EventDelegation']['event_id']); + $event_id = $this->Event->savePreparedEvent($event); + return $event_id; + } + + private function __prepareEvent(&$event) { + $objects = array('Attribute', 'ShadowAttribute', 'EventTag'); + $objects = array( + 'Attribute' => array('id', 'event_id'), + 'EventTag' => array('id', 'event_id'), + 'ShadowAttribute' => array('id', 'event_id'), + ); + $objectsWithAttachments = array('Attribute', 'ShadowAttribute'); + $objectsToRearrange = array('Attribute', 'ShadowAttribute', 'EventTag'); + unset ($event['Event']['id']); + foreach ($objects as $object_type => $fields) { + foreach ($event[$object_type] as &$object) { + // append attachment + if (in_array($object_type, $objectsWithAttachments)) { + if ($this->Event->Attribute->typeIsAttachment($object['type'])) { + $encodedFile = $this->Event->$object_type->base64EncodeAttachment($object); + $object['data'] = $encodedFile; + } + } + + // unset ID fields and relations + foreach ($fields as $field) { + unset($object[$field]); + } + } + if (in_array($object_type, $objectsToRearrange)) { + $event['Event'][$object_type] = $event[$object_type]; + unset($event[$object_type]); + } + } + } } \ No newline at end of file diff --git a/app/Model/Log.php b/app/Model/Log.php index 74bd7b9cd..c71cffc91 100755 --- a/app/Model/Log.php +++ b/app/Model/Log.php @@ -41,7 +41,9 @@ class Log extends AppModel { 'reset_auth_key', 'update', 'enable', - 'disable' + 'disable', + 'accept_delegation', + 'request_delegation' )), 'message' => 'Options : ...' ) diff --git a/app/Model/Server.php b/app/Model/Server.php index 42a52b309..8c050e5b7 100755 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -495,6 +495,15 @@ class Server extends AppModel { 'type' => 'boolean', 'null' => true ), + 'delegation' => array( + 'level' => 1, + 'description' => 'This feature allows users to created org only events and ask another organisation to take owenership of the event. This allows organisations to remain anonymous by asking a partner to publish an event for them.', + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), ), 'GnuPG' => array( 'branch' => 1, diff --git a/app/View/Elements/dashboard/dashboard_events.ctp b/app/View/Elements/dashboard/dashboard_events.ctp new file mode 100644 index 000000000..ddb4f61b5 --- /dev/null +++ b/app/View/Elements/dashboard/dashboard_events.ctp @@ -0,0 +1,13 @@ +
+

Changes since last visit

+

+ Events updated: (View)
+ Events published: (View)
+

+
+ \ No newline at end of file diff --git a/app/View/Elements/dashboard/dashboard_notifications.ctp b/app/View/Elements/dashboard/dashboard_notifications.ctp new file mode 100644 index 000000000..cc6e2adc6 --- /dev/null +++ b/app/View/Elements/dashboard/dashboard_notifications.ctp @@ -0,0 +1,14 @@ +
+

Notifications

+

+ Proposals: (View)
+ Events with proposals: (View)
+ Delegation requests: (View) +

+
+ \ No newline at end of file diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp index 1a79cba67..dc8a39eda 100755 --- a/app/View/Elements/global_menu.ctp +++ b/app/View/Elements/global_menu.ctp @@ -71,6 +71,7 @@