Merge branch 'feature/proposalfix' into develop

pull/304/merge
iglocska 2014-08-19 08:43:24 +02:00
commit c535b940ab
17 changed files with 802 additions and 253 deletions

View File

@ -252,6 +252,8 @@ CREATE TABLE IF NOT EXISTS `shadow_attributes` (
`event_org` varchar(255) COLLATE utf8_bin NOT NULL,
`comment` text COLLATE utf8_bin NOT NULL,
`event_uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`deleted` tinyint(1) NOT NULL DEFAULT `0`,
`timestamp` int(11) NOT NULL DEFAULT `0`,
PRIMARY KEY (`id`),
KEY `event_id` (`event_id`),
KEY `uuid` (`uuid`),

125
INSTALL/upgrade_2.3.sql Normal file
View File

@ -0,0 +1,125 @@
-- Copyright (c) 2009 www.cryer.co.uk
-- Script is free to use provided this copyright header is included.
drop procedure if exists AddColumnUnlessExists;
delimiter '//'
create procedure AddColumnUnlessExists(
IN dbName tinytext,
IN tableName tinytext,
IN fieldName tinytext,
IN fieldDef text)
begin
IF NOT EXISTS (
SELECT * FROM information_schema.COLUMNS
WHERE column_name=fieldName
and table_name=tableName
and table_schema=dbName
)
THEN
set @ddl=CONCAT('ALTER TABLE ',dbName,'.',tableName,
' ADD COLUMN ',fieldName,' ',fieldDef);
prepare stmt from @ddl;
execute stmt;
END IF;
end;
//
delimiter ';'
call AddColumnUnlessExists(Database(), 'roles', 'perm_template', 'TINYINT( 1 ) NOT NULL DEFAULT 0');
call AddColumnUnlessExists(Database(), 'shadow_attributes', 'deleted', 'TINYINT( 1 ) NOT NULL DEFAULT 0');
call AddColumnUnlessExists(Database(), 'shadow_attributes', 'timestamp', 'INT( 11 ) NOT NULL DEFAULT 0');
-- --------------------------------------------------------
--
-- Table structure for table `templates`
--
CREATE TABLE IF NOT EXISTS `templates` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`org` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`share` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `template_elements`
--
CREATE TABLE IF NOT EXISTS `template_elements` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`template_id` int(11) NOT NULL,
`position` int(11) NOT NULL,
`element_definition` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `template_element_attributes`
--
CREATE TABLE IF NOT EXISTS `template_element_attributes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`template_element_id` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`description` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`to_ids` tinyint(1) NOT NULL DEFAULT '1',
`category` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`complex` tinyint(1) NOT NULL,
`type` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`mandatory` tinyint(1) NOT NULL,
`batch` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `template_element_files`
--
CREATE TABLE IF NOT EXISTS `template_element_files` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`template_element_id` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`description` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`category` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`malware` tinyint(1) NOT NULL,
`mandatory` tinyint(1) NOT NULL,
`batch` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `template_element_texts`
--
CREATE TABLE IF NOT EXISTS `template_element_texts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`template_element_id` int(11) NOT NULL,
`text` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `template_tags`
--
CREATE TABLE IF NOT EXISTS `template_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`template_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

View File

@ -190,7 +190,8 @@ class AppController extends Controller {
'recursive' => -1,
'fields' => array('event_id', 'event_org'),
'conditions' => array(
'ShadowAttribute.event_org' => $this->Auth->user('org')
'ShadowAttribute.event_org' => $this->Auth->user('org'),
'ShadowAttribute.deleted' => 0,
)));
$results = array();
$eventIds = array();

View File

@ -1885,6 +1885,12 @@ class AttributesController extends AppController {
$success = true;
if (($results['created'] > 0 || $results['deleted'] > 0) && $results['createdFail'] == 0 && $results['deletedFail'] == 0) {
$message .= 'Update completed without any issues.';
$event = $this->Attribute->Event->find('first', array(
'conditions' => array('Event.id' => $id),
'recursive' => -1
));
$event['Event']['published'] = 0;
$this->Attribute->Event->save($event);
} else {
$message .= 'Update completed with some errors.';
$success = false;

View File

@ -204,7 +204,31 @@ class EventsController extends AppController {
'ThreatLevel.name'))
),
));
$this->set('events', $this->paginate());
// for rest, don't use the pagination. With this, we'll escape the limit of events shown on the index.
if ($this->_isRest()) {
$rules = array();
$fieldNames = array_keys($this->Event->getColumnTypes());
$directions = array('ASC', 'DESC');
if (isset($this->passedArgs['sort']) && in_array($this->passedArgs['sort'], $fieldNames)) {
if (isset($this->passedArgs['direction']) && in_array(strtoupper($this->passedArgs['direction']), $directions)) {
$rules['order'] = array($this->passedArgs['sort'] => $this->passedArgs['direction']);
} else {
$rules['order'] = array($this->passedArgs['sort'] => 'ASC');
}
} else {
$rules['order'] = array('Event.id' => 'DESC');
}
if (isset($this->passedArgs['limit'])) {
$rules['limit'] = intval($this->passedArgs['limit']);
}
$rules['contain'] = $this->paginate['contain'];
if (isset($this->paginate['conditions'])) $rules['conditions'] = $this->paginate['conditions'];
$events = $this->Event->find('all', $rules);
$this->set('events', $events);
} else {
$this->set('events', $this->paginate());
}
if (!$this->Event->User->getPGP($this->Auth->user('id')) && Configure::read('GnuPG.onlyencrypted') == 'true') {
$this->Session->setFlash(__('No GPG key set in your profile. To receive emails, submit your public key in your profile.'));
}
@ -344,7 +368,7 @@ class EventsController extends AppController {
}
$this->loadModel('Log');
$logEntries = $this->Log->find('all', array(
'conditions' => array('model LIKE' => '%ShadowAttribute%', 'org !=' => $results[0]['Event']['orgc'], 'title LIKE' => '%Event (' . $id . ')%'),
'conditions' => array('model' => 'ShadowAttribute', 'org !=' => $results[0]['Event']['orgc'], 'title LIKE' => '%Event (' . $id . ')%'),
'fields' => array('org'),
'group' => 'org'
));
@ -976,7 +1000,8 @@ class EventsController extends AppController {
} else throw new MethodNotAllowedException('Event could not be saved: Could not find the local event.');
$fieldList = array(
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'from', 'distribution', 'timestamp'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp', 'comment')
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp', 'comment'),
'ShadowAttribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'org', 'event_org', 'comment', 'event_uuid', 'deleted', 'to_ids', 'uuid')
);
$c = 0;
@ -998,6 +1023,29 @@ class EventsController extends AppController {
$c++;
}
}
// check if the exact proposal exists, if yes check if the incoming one is deleted or not. If it is deleted, remove the old proposal and replace it with the one marked for being deleted
// otherwise throw the new one away.
if (isset($this->request->data['ShadowAttribute'])) {
foreach ($this->request->data['ShadowAttribute'] as $k => &$proposal) {
$existingProposal = $this->Event->ShadowAttribute->find('first', array(
'recursive' => -1,
'conditions' => array(
'value' => $proposal['value'],
'category' => $proposal['category'],
'to_ids' => $proposal['to_ids'],
'type' => $proposal['type'],
'event_uuid' => $proposal['event_uuid'],
'uuid' => $proposal['uuid']
)
));
if ($existingProposal['ShadowAttribute']['deleted'] == 1) {
$this->Event->ShadowAttribute->delete($existingProposal['ShadowAttribute']['id'], false);
} else {
unset($this->request->data['ShadowAttribute'][$k]);
}
}
}
// this saveAssociated() function will save not only the event, but also the attributes
// from the attributes attachments are also saved to the disk thanks to the afterSave() fonction of Attribute
if ($saveEvent) {
@ -1005,6 +1053,7 @@ class EventsController extends AppController {
} else {
throw new MethodNotAllowedException();
}
if ($saveResult) {
// TODO RESTfull: we now need to compare attributes, to see if we need to do a RESTfull attribute delete
$message = 'Saved';
@ -1186,6 +1235,10 @@ class EventsController extends AppController {
$this->Session->setFlash(__('Job queued.'));
}
$this->redirect(array('action' => 'view', $id));
} else {
$this->set('id', $id);
$this->set('type', 'publish');
$this->render('ajax/eventPublishConfirmationForm');
}
}
@ -1244,6 +1297,10 @@ class EventsController extends AppController {
$this->Session->setFlash(__('Sending of email failed', true), 'default', array(), 'error');
}
$this->redirect(array('action' => 'view', $id));
} else {
$this->set('id', $id);
$this->set('type', 'alert');
$this->render('ajax/eventPublishConfirmationForm');
}
}
@ -2252,12 +2309,13 @@ class EventsController extends AppController {
public function proposalEventIndex() {
$this->loadModel('ShadowAttribute');
$this->ShadowAttribute->recursive = -1;
$conditions = array('ShadowAttribute.deleted' => 0);
if (!$this->_isSiteAdmin()) $conditions[] = array('ShadowAttribute.event_org' => $this->Auth->user('org'));
$result = $this->ShadowAttribute->find('all', array(
'fields' => array('event_id'),
'group' => 'event_id',
'conditions' => array(
'ShadowAttribute.event_org =' => $this->Auth->user('org'),
)));
'conditions' => $conditions
));
$this->Event->recursive = -1;
$conditions = array();
foreach ($result as $eventId) {
@ -2278,6 +2336,9 @@ class EventsController extends AppController {
'fields' => array(
'ShadowAttribute.id', 'ShadowAttribute.org', 'ShadowAttribute.event_id'
),
'conditions' => array(
'ShadowAttribute.deleted' => 0
),
),
));
$events = $this->paginate();
@ -2451,7 +2512,7 @@ class EventsController extends AppController {
$event = $this->Event->find('first', array(
'conditions' => array('id' => $id),
'recursive' => -1,
'fields' => array('orgc', 'id', 'distribution'),
'fields' => array('orgc', 'id', 'distribution', 'published'),
));
if (!$this->_isSiteAdmin() && !empty($event) && $event['Event']['orgc'] != $this->Auth->user('org')) throw new MethodNotAllowedException('Event not found or you don\'t have permissions to create attributes');
$saved = 0;
@ -2469,6 +2530,14 @@ class EventsController extends AppController {
}
}
}
if ($saved > 0 && $event['Event']['published'] == 1) {
$event = $this->Event->find('first', array(
'conditions' => array('Event.id' => $id),
'recursive' => -1
));
$event['Event']['published'] = 0;
$this->Event->save($event);
}
$this->Session->setFlash($saved . ' attributes created. ' . $failed . ' attributes could not be saved. This may be due to attributes with similar values already existing.');
$this->redirect(array('controller' => 'events', 'action' => 'view', $id));
} else {
@ -2536,4 +2605,100 @@ class EventsController extends AppController {
throw new Exception(h($result['message']));
}
}
public function filterEventIdsForPush() {
if (!$this->userRole['perm_sync']) throw new MethodNotAllowedException('You do not have the permission to do that.');
if ($this->request->is('post')) {
$incomingIDs = array();
$incomingEvents = array();
foreach ($this->request->data as $event) {
$incomingIDs[] = $event['Event']['uuid'];
$incomingEvents[$event['Event']['uuid']] = $event['Event']['timestamp'];
}
$events = $this->Event->find('all', array(
'conditions' => array('Event.uuid' => $incomingIDs),
'recursive' => -1,
'fields' => array('Event.uuid', 'Event.timestamp', 'Event.locked'),
));
foreach ($events as $k => $v) {
if (!$v['Event']['timestamp'] < $incomingEvents[$v['Event']['uuid']]) {
unset($incomingEvents[$v['Event']['uuid']]);
continue;
}
if ($v['Event']['locked'] == 0) {
unset($incomingEvents[$v['Event']['uuid']]);
}
}
$this->set('result', array_keys($incomingEvents));
}
}
public function checkuuid($uuid) {
if (!$this->userRole['perm_sync']) throw new MethodNotAllowedException('You do not have the permission to do that.');
$events = $this->Event->find('first', array(
'conditions' => array('Event.uuid' => $uuid),
'recursive' => -1,
'fields' => array('Event.uuid'),
));
$this->set('result', array('result' => empty($events)));
}
public function pushProposals($uuid) {
$message= "";
$success = true;
$counter = 0;
if (!$this->userRole['perm_sync']) throw new MethodNotAllowedException('You do not have the permission to do that.');
if ($this->request->is('post')) {
$event = $this->Event->find('first', array(
'conditions' => array('Event.uuid' => $uuid),
'contains' => array('ShadowAttribute', 'Attribute' => array(
'fields' => array('id', 'uuid', 'event_id'),
)),
'fields' => array('Event.uuid', 'Event.id'),
));
if (empty($event)) {
$message = "Event not found.";
$success = false;
} else {
foreach ($this->request->data as $k => $sa) {
if (isset($event['ShadowAttribute'])) {
foreach ($event['ShadowAttribute'] as $oldk => $oldsa) {
$temp = json_encode($oldsa);
if ($sa['event_uuid'] == $oldsa['event_uuid'] && $sa['value'] == $oldsa['value'] && $sa['type'] == $oldsa['type'] && $sa['category'] == $oldsa['category'] && $sa['to_ids'] == $oldsa['to_ids']) {
if ($oldsa['timestamp'] < $sa['timestamp']) $this->Event->ShadowAttribute->delete($oldsa['id']);
else continue 2;
}
}
}
$sa['event_id'] = $event['Event']['id'];
if ($sa['old_id'] != 0) {
foreach($event['Attribute'] as $attribute) {
if ($sa['uuid'] == $attribute['uuid']) {
$sa['old_id'] = $attribute['id'];
}
}
}
if (isset($sa['id'])) unset($sa['id']);
$this->Event->ShadowAttribute->create();
if (!$this->Event->ShadowAttribute->save(array('ShadowAttribute' => $sa))) {
$message = "Some of the proposals could not be saved.";
$success = false;
} else {
$counter++;
}
//if (!$sa['deleted']) $this->Event->ShadowAttribute->__sendProposalAlertEmail($event['Event']['id']);
}
}
if ($success) {
if ($counter) {
$message = $counter . " Proposal(s) added.";
} else {
$message = "Nothing to update.";
}
}
$this->set('data', array('success' => $success, 'message' => $message, 'counter' => $counter));
$this->set('_serialize', 'data');
}
}
}

View File

@ -95,11 +95,12 @@ class ShadowAttributesController extends AppController {
$activeAttribute['Attribute']['value'] = $shadow['value'];
$activeAttribute['Attribute']['type'] = $shadow['type'];
$activeAttribute['Attribute']['category'] = $shadow['category'];
$activeAttribute['Attribute']['comment'] = $shadow['comment'];
$activeAttribute['Attribute']['to_ids'] = $shadow['to_ids'];
$date = new DateTime();
$activeAttribute['Attribute']['timestamp'] = $date->getTimestamp();
$this->Attribute->save($activeAttribute['Attribute']);
$this->ShadowAttribute->delete($id, $cascade = false);
$this->ShadowAttribute->setDeleted($id);
$this->loadModel('Event');
$this->Event->Behaviors->detach('SysLogLogable.SysLogLogable');
$this->Event->recursive = -1;
@ -152,10 +153,12 @@ class ShadowAttributesController extends AppController {
if ($this->ShadowAttribute->typeIsAttachment($shadow['type'])) {
$this->_moveFile($toDeleteId, $this->Attribute->id, $shadow['event_id']);
}
$this->ShadowAttribute->delete($toDeleteId, $cascade = false);
$this->ShadowAttribute->setDeleted($toDeleteId);
$fieldList = array('proposal_email_lock', 'id', 'info', 'published');
$event['Event']['proposal_email_lock'] = 0;
if ($this->Auth->user('org') == $event['Event']['orgc']) {
$event['Event']['proposal_email_lock'] = 0;
}
$event['Event']['published'] = 0;
$this->autoRender = false;
if ($this->Event->save($event, array('fieldList' => $fieldList))) {
@ -216,8 +219,10 @@ class ShadowAttributesController extends AppController {
$this->redirect(array('controller' => 'events', 'action' => 'view', $eventId));
}
}
if ($this->ShadowAttribute->delete($id, $cascade = false)) {
$this->_setProposalLock($eventId, false);
if ($this->ShadowAttribute->setDeleted($id)) {
if ($this->Auth->user('org') == $this->Event->data['Event']['orgc']) {
$this->_setProposalLock($eventId, false);
}
$this->autoRender = false;
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Proposal discarded.')),'status'=>200));
} else {
@ -322,8 +327,9 @@ class ShadowAttributesController extends AppController {
}
if ($successes) {
// list the ones that succeeded
$this->Session->setFlash(__('The lines' . $successes . ' have been saved', true));
$this->__sendProposalAlertEmail($eventId);
$emailResult = "";
if (!$this->__sendProposalAlertEmail($eventId) == false) $emailResult = " but sending out the alert e-mails has failed for at least one recipient.";
$this->Session->setFlash(__('The lines' . $successes . ' have been saved' . $emailResult, true));
}
}
@ -345,11 +351,15 @@ class ShadowAttributesController extends AppController {
$this->request->data['ShadowAttribute']['event_uuid'] = $event_uuid;
$this->request->data['ShadowAttribute']['event_org'] = $event_org;
if ($this->ShadowAttribute->save($this->request->data)) {
$this->__sendProposalAlertEmail($this->request->data['ShadowAttribute']['event_id']);
// list the ones that succeeded
$emailResult = "";
if (!$this->__sendProposalAlertEmail($this->request->data['ShadowAttribute']['event_id'])) {
$emailResult = " but sending out the alert e-mails has failed for at least one recipient.";
}
// inform the user and redirect
if ($this->request->is('ajax')) {
$this->autoRender = false;
return new CakeResponse(array('body'=> json_encode(array('saved' => true)),'status'=>200));
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Proposal added' . $emailResult)),'status'=>200));
} else {
$this->Session->setFlash(__('The proposal has been saved'));
$this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id']));
@ -470,12 +480,7 @@ class ShadowAttributesController extends AppController {
$this->request->data['ShadowAttribute']['org'] = $this->Auth->user('org');
$this->request->data['ShadowAttribute']['event_uuid'] = $event_uuid;
$this->request->data['ShadowAttribute']['event_org'] = $event_org;
if ($this->ShadowAttribute->save($this->request->data)) {
$this->__sendProposalAlertEmail($eventId);
} else {
$this->Session->setFlash(__('The ShadowAttribute could not be saved. Did you already upload this file?'));
$this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id']));
}
$this->ShadowAttribute->save($this->request->data);
// no errors in file upload, entry already in db, now move the file where needed and zip it if required.
// no sanitization is required on the filename, path or type as we save
@ -530,7 +535,11 @@ class ShadowAttributesController extends AppController {
}
// everything is done, now redirect to event view
$this->Session->setFlash(__('The attachment has been uploaded'));
$emailResult = "";
if (!$this->__sendProposalAlertEmail($eventId)) $emailResult = " but sending out the alert e-mails has failed for at least one recipient.";
$this->Session->setFlash(__('The attachment has been uploaded' . $emailResult));
$this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id']));
} else {
@ -623,8 +632,9 @@ class ShadowAttributesController extends AppController {
$this->request->data['ShadowAttribute']['email'] = $this->Auth->user('email');
$fieldList = array('category', 'type', 'value1', 'value2', 'to_ids', 'value', 'org');
if ($this->ShadowAttribute->save($this->request->data)) {
$this->__sendProposalAlertEmail($this->request->data['ShadowAttribute']['event_id']);
$this->Session->setFlash(__('The proposed Attribute has been saved'));
$emailResult = "";
if (!$this->__sendProposalAlertEmail($this->request->data['ShadowAttribute']['event_id'])) $emailResult = " but sending out the alert e-mails has failed for at least one recipient.";
$this->Session->setFlash(__('The proposed Attribute has been saved' . $emailResult));
$this->redirect(array('controller' => 'events', 'action' => 'view', $eventId));
} else {
$this->Session->setFlash(__('The ShadowAttribute could not be saved. Please, try again.'));
@ -679,86 +689,90 @@ class ShadowAttributesController extends AppController {
} else {
$this->_setProposalLock($id);
}
$this->loadModel('User');
$this->User->recursive = -1;
$orgMembers = array();
$temp = $this->User->findAllByOrg($event['Event']['orgc'], array('email', 'gpgkey', 'contactalert', 'id'));
foreach ($temp as $tempElement) {
if ($tempElement['User']['contactalert'] || $tempElement['User']['id'] == $event['Event']['user_id']) {
array_push($orgMembers, $tempElement);
}
}
$body = "";
$body .= "Hello, \n";
$body .= "\n";
$body .= "A user of another organisation has proposed a change to an event created by you or your organisation. \n";
$body .= "\n";
$body .= "To view the event in question, follow this link:";
$body .= ' ' . Configure::read('MISP.baseurl') . '/events/view/' . $id . "\n";
$body .= "\n";
$body .= "You can reach the user at " . $this->Auth->user('email');
$body .= "\n";
// sign the body
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'))); // , 'debug' => true
$gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password'));
$bodySigned = $gpg->sign($body, Crypt_GPG::SIGN_MODE_CLEAR);
// Add the GPG key of the user as attachment
// LATER sign the attached GPG key
if (null != (!$this->User->getPGP($this->Auth->user('id')))) {
// save the gpg key to a temporary file
$tmpfname = tempnam(TMP, "GPGkey");
$handle = fopen($tmpfname, "w");
fwrite($handle, $this->User->getPGP($this->Auth->user('id')));
fclose($handle);
// attach it
$this->Email->attachments = array(
'gpgkey.asc' => $tmpfname
);
}
foreach ($orgMembers as &$reporter) {
if (!empty($reporter['User']['gpgkey'])) {
// import the key of the user into the keyring
// this isn't really necessary, but it gives it the fingerprint necessary for the next step
$keyImportOutput = $gpg->importKey($reporter['User']['gpgkey']);
// say what key should be used to encrypt
try {
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir')));
$gpg->addEncryptKey($keyImportOutput['fingerprint']); // use the key that was given in the import
$bodyEncSig = $gpg->encrypt($bodySigned, true);
} catch (Exception $e){
// catch errors like expired PGP keys
$this->log($e->getMessage());
// no need to return here, as we want to send out mails to the other users if GPG encryption fails for a single user
try {
$this->loadModel('User');
$this->User->recursive = -1;
$orgMembers = array();
$temp = $this->User->findAllByOrg($event['Event']['orgc'], array('email', 'gpgkey', 'contactalert', 'id'));
foreach ($temp as $tempElement) {
if ($tempElement['User']['contactalert'] || $tempElement['User']['id'] == $event['Event']['user_id']) {
array_push($orgMembers, $tempElement);
}
} else {
$bodyEncSig = $bodySigned;
// FIXME should I allow sending unencrypted "contact" mails to people if they didn't import they GPG key?
}
// prepare the email
$this->Email->from = Configure::read('MISP.email');
$this->Email->to = $reporter['User']['email'];
$this->Email->subject = "[" . Configure::read('MISP.org') . " " . Configure::read('MISP.name') . "] Proposal to event #" . $id;
$this->Email->template = 'body';
$this->Email->sendAs = 'text'; // both text or html
$this->set('body', $bodyEncSig);
$body = "";
$body .= "Hello, \n";
$body .= "\n";
$body .= "A user of another organisation has proposed a change to an event created by you or your organisation. \n";
$body .= "\n";
$body .= "To view the event in question, follow this link:";
$body .= ' ' . Configure::read('MISP.baseurl') . '/events/view/' . $id . "\n";
$body .= "\n";
$body .= "You can reach the user at " . $this->Auth->user('email');
$body .= "\n";
// sign the body
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'))); // , 'debug' => true
$gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password'));
$bodySigned = $gpg->sign($body, Crypt_GPG::SIGN_MODE_CLEAR);
// Add the GPG key of the user as attachment
// LATER sign the attached GPG key
if (null != ($this->User->getPGP($this->Auth->user('id')))) {
// attach the gpg key
if (null != (!$this->User->getPGP($this->Auth->user('id')))) {
// save the gpg key to a temporary file
$tmpfname = tempnam(TMP, "GPGkey");
$handle = fopen($tmpfname, "w");
fwrite($handle, $this->User->getPGP($this->Auth->user('id')));
fclose($handle);
// attach it
$this->Email->attachments = array(
'gpgkey.asc' => $tmpfname
'gpgkey.asc' => $tmpfname
);
}
// send it
$result = $this->Email->send();
// If you wish to send multiple emails using a loop, you'll need
// to reset the email fields using the reset method of the Email component.
$this->Email->reset();
foreach ($orgMembers as &$reporter) {
if (!empty($reporter['User']['gpgkey'])) {
// import the key of the user into the keyring
// this isn't really necessary, but it gives it the fingerprint necessary for the next step
$keyImportOutput = $gpg->importKey($reporter['User']['gpgkey']);
// say what key should be used to encrypt
try {
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir')));
$gpg->addEncryptKey($keyImportOutput['fingerprint']); // use the key that was given in the import
$bodyEncSig = $gpg->encrypt($bodySigned, true);
} catch (Exception $e){
// catch errors like expired PGP keys
$this->log($e->getMessage());
// no need to return here, as we want to send out mails to the other users if GPG encryption fails for a single user
}
} else {
$bodyEncSig = $bodySigned;
// FIXME should I allow sending unencrypted "contact" mails to people if they didn't import they GPG key?
}
// prepare the email
$this->Email->from = Configure::read('MISP.email');
$this->Email->to = $reporter['User']['email'];
$this->Email->subject = "[" . Configure::read('MISP.org') . " " . Configure::read('MISP.name') . "] Proposal to event #" . $id;
$this->Email->template = 'body';
$this->Email->sendAs = 'text'; // both text or html
$this->set('body', $bodyEncSig);
// Add the GPG key of the user as attachment
// LATER sign the attached GPG key
if (null != ($this->User->getPGP($this->Auth->user('id')))) {
// attach the gpg key
$this->Email->attachments = array(
'gpgkey.asc' => $tmpfname
);
}
// send it
$result = $this->Email->send();
// If you wish to send multiple emails using a loop, you'll need
// to reset the email fields using the reset method of the Email component.
$this->Email->reset();
}
} catch (Exception $e) {
return false;
}
return true;
}
public function index() {
@ -766,9 +780,10 @@ class ShadowAttributesController extends AppController {
if (!$this->_isSiteAdmin()) {
$conditions = array('Event.org =' => $this->Auth->user('org'));
}
$conditions[] = array('deleted' => 0);
$this->paginate = array(
'conditions' => $conditions,
'fields' => array('id', 'org', 'old_id'),
'fields' => array('id', 'org', 'old_id', 'deleted', 'value', 'category', 'type'),
'contain' => array(
'Event' => array(
'fields' => array('id', 'org', 'info', 'orgc'),
@ -779,19 +794,6 @@ class ShadowAttributesController extends AppController {
$this->set('shadowAttributes', $this->paginate());
}
public function eventIndex() {
$result = $this->ShadowAttribute->find('all', array(
'fields' => array('event_id'),
'group' => 'event_id',
'conditions' => array(
'ShadowAttribute.event_org =' => $this->Auth->user('org'),
)));
$this->loadModel('Event');
foreach ($result as $eventId) {
}
}
private function _getEventData($event_id) {
$this->loadModel('Event');
$this->Event->recursive = -1;

View File

@ -346,6 +346,12 @@ class TemplatesController extends AppController {
if (!$this->Attribute->save(array('Attribute' => $attribute))) $fails++;
}
$count = $k + 1;
$event = $this->Event->find('first', array(
'conditions' => array('Event.id' => $event_id),
'recursive' => -1
));
$event['Event']['published'] = 0;
$this->Event->save($event);
if ($fails == 0) $this->Session->setFlash(__('Event populated, ' . $count . ' attributes successfully created.'));
else $this->Session->setFlash(__('Event populated, but ' . $fails . ' attributes could not be saved.'));
$this->redirect(array('controller' => 'events', 'action' => 'view', $event_id));

View File

@ -28,7 +28,7 @@ class Event extends AppModel {
public $virtualFields = array();
public $mispVersion = '2.2.0';
public $mispVersion = '2.3.0';
/**
* Description field
@ -456,18 +456,21 @@ class Event extends AppModel {
* @throws InternalErrorException
*/
public function cleanupEventArrayFromXML(&$data) {
// Workaround for different structure in XML/array than what CakePHP expects
if (isset($data['Event']['Attribute']) && is_array($data['Event']['Attribute']) && count($data['Event']['Attribute'])) {
if (is_numeric(implode(array_keys($data['Event']['Attribute']), ''))) {
// normal array of multiple Attributes
$data['Attribute'] = $data['Event']['Attribute'];
} else {
// single attribute
$data['Attribute'][0] = $data['Event']['Attribute'];
$objects = array('Attribute', 'ShadowAttribute');
foreach ($objects as $object) {
// Workaround for different structure in XML/array than what CakePHP expects
if (isset($data['Event'][$object]) && is_array($data['Event'][$object]) && count($data['Event'][$object])) {
if (is_numeric(implode(array_keys($data['Event'][$object]), ''))) {
// normal array of multiple Attributes
$data[$object] = $data['Event'][$object];
} else {
// single attribute
$data[$object][0] = $data['Event'][$object];
}
}
unset($data['Event'][$object]);
}
unset($data['Event']['Attribute']);
return $data;
}
@ -587,47 +590,52 @@ class Event extends AppModel {
// LATER try to do this using a separate EventsController and renderAs() function
$xmlArray = array();
// rearrange things to be compatible with the Xml::fromArray()
$event['Event']['Attribute'] = $event['Attribute'];
unset($event['Attribute']);
if (isset($event['Attribute'])) {
$event['Event']['Attribute'] = $event['Attribute'];
unset($event['Attribute']);
}
// cleanup the array from things we do not want to expose
//unset($event['Event']['org']);
// remove value1 and value2 from the output
foreach ($event['Event']['Attribute'] as $key => &$attribute) {
// do not keep attributes that are private, nor cluster
if ($attribute['distribution'] < 2) {
unset($event['Event']['Attribute'][$key]);
continue; // stop processing this
if (isset($event['Event']['Attribute'])) {
foreach ($event['Event']['Attribute'] as $key => &$attribute) {
// do not keep attributes that are private, nor cluster
if ($attribute['distribution'] < 2) {
unset($event['Event']['Attribute'][$key]);
continue; // stop processing this
}
// Distribution, correct Connected Community to Community in Attribute
if ($attribute['distribution'] == 2) {
$attribute['distribution'] = 1;
}
// remove value1 and value2 from the output
unset($attribute['value1']);
unset($attribute['value2']);
// also add the encoded attachment
if ($this->Attribute->typeIsAttachment($attribute['type'])) {
$encodedFile = $this->Attribute->base64EncodeAttachment($attribute);
$attribute['data'] = $encodedFile;
}
// Passing the attribute ID together with the attribute could cause the deletion of attributes after a publish/push
// Basically, if the attribute count differed between two instances, and the instance with the lower attribute
// count pushed, the old attributes with the same ID got overwritten. Unsetting the ID before pushing it
// solves the issue and a new attribute is always created.
unset($attribute['id']);
}
// Distribution, correct Connected Community to Community in Attribute
if ($attribute['distribution'] == 2) {
$attribute['distribution'] = 1;
}
// remove value1 and value2 from the output
unset($attribute['value1']);
unset($attribute['value2']);
// also add the encoded attachment
if ($this->Attribute->typeIsAttachment($attribute['type'])) {
$encodedFile = $this->Attribute->base64EncodeAttachment($attribute);
$attribute['data'] = $encodedFile;
}
// Passing the attribute ID together with the attribute could cause the deletion of attributes after a publish/push
// Basically, if the attribute count differed between two instances, and the instance with the lower attribute
// count pushed, the old attributes with the same ID got overwritten. Unsetting the ID before pushing it
// solves the issue and a new attribute is always created.
unset($attribute['id']);
}
// Distribution, correct All to Community in Event
if ($event['Event']['distribution'] == 2) {
$event['Event']['distribution'] = 1;
}
// display the XML to the user
$xmlArray['Event'][] = $event['Event'];
$xmlObject = Xml::fromArray($xmlArray, array('format' => 'tags'));
$eventsXml = $xmlObject->asXML();
// do a REST POST request with the server
$data = $eventsXml;
// LATER validate HTTPS SSL certificate
$this->Dns = ClassRegistry::init('Dns');
if ($this->Dns->testipaddress(parse_url($uri, PHP_URL_HOST))) {
@ -718,7 +726,7 @@ class Event extends AppModel {
* TODO move this to a component
* @return array|NULL
*/
public function downloadEventFromServer($eventId, $server, $HttpSocket=null, $propsalDownload = false) {
public function downloadEventFromServer($eventId, $server, $HttpSocket=null, $proposalDownload = false) {
$url = $server['Server']['url'];
$authkey = $server['Server']['authkey'];
if (null == $HttpSocket) {
@ -737,7 +745,7 @@ class Event extends AppModel {
//'Connection' => 'keep-alive' // LATER followup cakephp ticket 2854 about this problem http://cakephp.lighthouseapp.com/projects/42648-cakephp/tickets/2854
)
);
if (!$propsalDownload) {
if (!$proposalDownload) {
$uri = $url . '/events/' . $eventId;
} else {
$uri = $url . '/shadow_attributes/getProposalsByUuid/' . $eventId;
@ -758,7 +766,7 @@ class Event extends AppModel {
* TODO move this to a component
* @return array of event_ids
*/
public function getEventIdsFromServer($server, $HttpSocket=null) {
public function getEventIdsFromServer($server, $all = false, $HttpSocket=null) {
$url = $server['Server']['url'];
$authkey = $server['Server']['authkey'];
@ -778,7 +786,7 @@ class Event extends AppModel {
//'Connection' => 'keep-alive' // LATER followup cakephp ticket 2854 about this problem http://cakephp.lighthouseapp.com/projects/42648-cakephp/tickets/2854
)
);
$uri = $url . '/events/index/sort:id/direction:desc/limit:9999'; // LATER verify if events are missing because we only selected the last 999
$uri = $url . '/events/index';
try {
$response = $HttpSocket->get($uri, $data = '', $request);
if ($response->isOk()) {
@ -792,11 +800,9 @@ class Event extends AppModel {
$eventArray['response']['Event'][0] = $tmp;
}
$eventIds = array();
// different actions if it's only 1 event or more
// only one event.
if (isset($eventArray['response']['Event']['id'])) {
if ($this->checkIfNewer($eventArray['response']['Event'])) {
$eventIds[] = $eventArray['response']['Event']['id'];
if ($all) {
foreach ($eventArray['response']['Event'] as $event) {
$eventIds[] = $event['uuid'];
}
} else {
// multiple events, iterate over the array
@ -914,6 +920,7 @@ class Event extends AppModel {
),
'ShadowAttribute' => array(
'fields' => $fieldsShadowAtt,
'conditions' => array('deleted' => 0),
),
)
);
@ -1502,10 +1509,9 @@ class Event extends AppModel {
$this->recursive = 1;
$this->read();
$this->data['Event']['locked'] = 1;
// get a list of the servers
$server = ClassRegistry::init('Server');
$servers = $server->find('all', array(
$serverModel = ClassRegistry::init('Server');
$servers = $serverModel->find('all', array(
'conditions' => array('Server.push' => true)
));
// iterate over the servers and upload the event
@ -1525,6 +1531,9 @@ class Event extends AppModel {
$uploaded = !$uploaded ? $uploaded : $thisUploaded;
$failedServers[] = $server['Server']['url'];
}
if (isset($this->data['ShadowAttribute'])) {
$serverModel->syncProposals($HttpSocket, $server, null, $id, $this);
}
}
}
if (!$uploaded) {

View File

@ -257,38 +257,36 @@ class Server extends AppModel {
// increment lastid based on the highest ID seen
$this->save($event, array('fieldList' => array('lastpulledid', 'url')));
// grab all of the shadow attributes that are relevant to us
$events = $eventModel->find('all', array(
'fields' => array('id', 'uuid'),
'recursive' => -1,
));
$shadowAttribute = ClassRegistry::init('ShadowAttribute');
$shadowAttribute->recursive = -1;
foreach ($events as &$event) {
$proposals = $eventModel->downloadEventFromServer($event['Event']['uuid'], $server, null, true);
if (null != $proposals) {
if (isset($proposals['ShadowAttribute']['id'])) {
$temp = $proposals['ShadowAttribute'];
$proposals['ShadowAttribute'] = array(0 => $temp);
}
foreach($proposals['ShadowAttribute'] as &$proposal) {
unset($proposal['id']);
$proposal['event_id'] = $event['Event']['id'];
if (!$shadowAttribute->findByUuid($proposal['uuid'])) {
if (isset($pulledProposals[$event['Event']['id']])) {
$pulledProposals[$event['Event']['id']]++;
} else {
$pulledProposals[$event['Event']['id']] = 1;
}
if (isset($proposal['old_id'])) {
$oldAttribute = $eventModel->Attribute->find('first', array('recursive' => -1, 'conditions' => array('uuid' => $proposal['uuid'])));
if ($oldAttribute) $proposal['old_id'] = $oldAttribute['Attribute']['id'];
else $proposal['old_id'] = 0;
}
$shadowAttribute->create();
$shadowAttribute->save($proposal);
}
}
}
$events = $eventModel->find('all', array(
'fields' => array('id', 'uuid'),
'recursive' => -1,
));
$shadowAttribute = ClassRegistry::init('ShadowAttribute');
$shadowAttribute->recursive = -1;
foreach ($events as &$event) {
$proposals = $eventModel->downloadEventFromServer($event['Event']['uuid'], $server, null, true);
if (null != $proposals) {
if (isset($proposals['ShadowAttribute']['id'])) {
$temp = $proposals['ShadowAttribute'];
$proposals['ShadowAttribute'] = array(0 => $temp);
}
foreach($proposals['ShadowAttribute'] as &$proposal) {
unset($proposal['id']);
$oldsa = $shadowAttribute->findOldProposal($proposal);
$proposal['event_id'] = $event['Event']['id'];
if (!$oldsa || $oldsa['timestamp'] < $proposal['timestamp']) {
if ($oldsa) $shadowAttribute->delete($oldsa['id']);
if (!isset($pulledProposals[$event['Event']['id']])) $pulledProposals[$event['Event']['id']] = 0;
$pulledProposals[$event['Event']['id']]++;
if (isset($proposal['old_id'])) {
$oldAttribute = $eventModel->Attribute->find('first', array('recursive' => -1, 'conditions' => array('uuid' => $proposal['uuid'])));
if ($oldAttribute) $proposal['old_id'] = $oldAttribute['Attribute']['id'];
else $proposal['old_id'] = 0;
}
$shadowAttribute->create();
$shadowAttribute->save($proposal);
}
}
}
@ -336,47 +334,55 @@ class Server extends AppModel {
'Event.attribute_count >' => 0
), //array of conditions
'recursive' => -1, //int
'fields' => array('Event.id'), //array of field names
'fields' => array('Event.id', 'Event.timestamp', 'Event.uuid'), //array of field names
);
$eventIds = $eventModel->find('all', $findParams);
}
$eventCount = count($eventIds);
//debug($eventIds);
// now process the $eventIds to pull each of the events sequentially
if (!empty($eventIds)) {
$successes = array();
$fails = array();
$lowestfailedid = null;
foreach ($eventIds as $k => $eventId) {
$eventModel->recursive=1;
$eventModel->contain(array('Attribute'));
$event = $eventModel->findById($eventId['Event']['id']);
$event['Event']['locked'] = true;
$result = $eventModel->uploadEventToServer(
$event,
$this->data,
$HttpSocket);
if ('Success' === $result) {
$successes[] = $event['Event']['id'];
$eventUUIDsFiltered = $this->filterEventIdsForPush($id, $HttpSocket, $eventIds);
if ($eventUUIDsFiltered === false) $pushFailed = true;
if (!empty($eventUUIDsFiltered)) {
$eventCount = count($eventUUIDsFiltered);
//debug($eventIds);
// now process the $eventIds to pull each of the events sequentially
if (!empty($eventUUIDsFiltered)) {
$successes = array();
$fails = array();
$lowestfailedid = null;
foreach ($eventUUIDsFiltered as $k => $eventUuid) {
$eventModel->recursive=1;
$eventModel->contain(array('Attribute'));
$event = $eventModel->findByUuid($eventUuid);
$event['Event']['locked'] = true;
$result = $eventModel->uploadEventToServer(
$event,
$this->data,
$HttpSocket);
if ('Success' === $result) {
$successes[] = $event['Event']['id'];
} else {
$fails[$event['Event']['id']] = $result;
}
if ($jobId && $k%10 == 0) {
$job->saveField('progress', 100 * $k / $eventCount);
}
}
if (count($fails) > 0) {
// there are fails, take the lowest fail
$lastpushedid = min(array_keys($fails));
} else {
$fails[$event['Event']['id']] = $result;
}
if ($jobId && $k%10 == 0) {
$job->saveField('progress', 100 * $k / $eventCount);
// no fails, take the highest success
$lastpushedid = max($successes);
}
// increment lastid based on the highest ID seen
// Save the entire Server data instead of just a single field, so that the logger can be fed with the extra fields.
$this->data['Server']['lastpushedid'] = $lastpushedid;
$this->save($this->data);
}
if (count($fails) > 0) {
// there are fails, take the lowest fail
$lastpushedid = min(array_keys($fails));
} else {
// no fails, take the highest success
$lastpushedid = max($successes);
}
// increment lastid based on the highest ID seen
// Save the entire Server data instead of just a single field, so that the logger can be fed with the extra fields.
$this->data['Server']['lastpushedid'] = $lastpushedid;
$this->save($this->data);
}
$this->syncProposals($HttpSocket, $this->data, null, null, $eventModel);
if (!isset($successes)) $successes = null;
if (!isset($fails)) $fails = null;
$this->Log = ClassRegistry::init('Log');
@ -398,4 +404,112 @@ class Server extends AppModel {
return array($successes, $fails);
}
}
public function filterEventIdsForPush($id, $HttpSocket, $eventIds) {
foreach ($eventIds as $k => $event) {
unset($eventIds[$k]['Event']['id']);
}
$server = $this->read(null, $id);
if (null == $HttpSocket) {
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocket($server);
}
$data = json_encode($eventIds);
$request = array(
'header' => array(
'Authorization' => $server['Server']['authkey'],
'Accept' => 'application/json',
'Content-Type' => 'application/json',
)
);
$uri = $server['Server']['url'] . '/events/filterEventIdsForPush';
$response = $HttpSocket->post($uri, $data, $request);
if ($response->code == '200') {
$uuidList = json_decode($response->body());
} else {
return false;
}
return $uuidList;
}
public function syncProposals($HttpSocket, $server, $sa_id = null, $event_id = null, $eventModel){
$saModel = ClassRegistry::init('ShadowAttribute');
if (null == $HttpSocket) {
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocket($server);
}
if ($sa_id == null) {
if ($event_id == null) {
// event_id is null when we are doing a push
$ids = $eventModel->getEventIdsFromServer($server, true, $HttpSocket);
$conditions = array('uuid' => $ids);
} else {
$conditions = array('id' => $event_id);
// event_id is not null when we are doing a publish
}
$events = $eventModel->find('all', array(
'conditions' => $conditions,
'recursive' => 1,
'contain' => 'ShadowAttribute',
'fields' => array('Event.uuid')
));
$fails = 0;
$success = 0;
$error_message = "";
$unchanged = array();
foreach ($events as $k => &$event) {
if (!empty($event['ShadowAttribute'])) {
foreach ($event['ShadowAttribute'] as &$sa) {
$sa['data'] = $saModel->base64EncodeAttachment($sa);
unset($sa['id']);
unset($sa['category_order']);
unset($sa['value1']);
unset($sa['value2']);
}
$data = json_encode($event['ShadowAttribute']);
$request = array(
'header' => array(
'Authorization' => $server['Server']['authkey'],
'Accept' => 'application/json',
'Content-Type' => 'application/json',
)
);
$uri = $server['Server']['url'] . '/events/pushProposals/' . $event['Event']['uuid'];
$response = $HttpSocket->post($uri, $data, $request);
if ($response->code == '200') {
$result = json_decode($response->body());
if ($result->success) {
$success += intval($result->counter);
} else {
$fails++;
if ($error_message == "") $result->message;
else $error_message += " --- " . $result->message;
}
} else {
$fails++;
}
}
}
} else {
// connect to checkuuid($uuid)
$request = array(
'header' => array(
'Authorization' => $server['Server']['authkey'],
'Accept' => 'application/json',
'Content-Type' => 'application/json',
)
);
$uri = $server['Server']['url'] . '/events/checkuuid/' . $sa_id;
$response = $HttpSocket->get($uri);
if ($response->code == '200') {
$uuidList = json_decode($response->body());
} else {
return false;
}
}
}
}

View File

@ -360,6 +360,11 @@ class ShadowAttribute extends AppModel {
if (!isset($this->data['ShadowAttribute']['type'])) {
return false;
}
if (empty($this->data['ShadowAttribute']['timestamp'])) {
$date = new DateTime();
$this->data['ShadowAttribute']['timestamp'] = $date->getTimestamp();
}
switch($this->data['ShadowAttribute']['type']) {
// lowercase these things
@ -429,7 +434,7 @@ class ShadowAttribute extends AppModel {
}
public function base64EncodeAttachment($attribute) {
$filepath = APP . "files" . DS . $attribute['event_id'] . DS . $attribute['id'];
$filepath = APP . "files" . DS . $attribute['event_id'] . DS . 'shadow' . DS. $attribute['id'];
$file = new File($filepath);
if (!$file->exists()) {
return '';
@ -439,7 +444,7 @@ class ShadowAttribute extends AppModel {
}
public function saveBase64EncodedAttachment($attribute) {
$rootDir = APP . DS . "files" . DS . $attribute['event_id'];
$rootDir = APP . DS . "files" . DS . 'shadow' . DS . $attribute['event_id'];
$dir = new Folder($rootDir, true); // create directory structure
$destpath = $rootDir . DS . $attribute['id'];
$file = new File ($destpath, true); // create the file
@ -529,5 +534,26 @@ class ShadowAttribute extends AppModel {
}
return $fails;
}
public function setDeleted($id) {
$this->id = $id;
$this->saveField('deleted', 1);
$date = new DateTime();
$this->saveField('timestamp', $date->getTimestamp());
}
public function findOldProposal($sa) {
$oldsa = $this->find('first', array(
'conditions' => array(
'event_uuid' => $sa['event_uuid'],
'value' => $sa['value'],
'type' => $sa['type'],
'category' => $sa['category'],
'to_ids' => $sa['to_ids'],
),
));
if (empty($oldsa)) return false;
else return $oldsa['ShadowAttribute'];
}
}

View File

@ -33,18 +33,23 @@
<li <?php if ($menuItem === 'proposeAttachment') echo 'class="active"';?>><a href="/shadow_attributes/add_attachment/<?php echo $event['Event']['id'];?>">Propose Attachment</a></li>
<?php endif; ?>
<li class="divider"></li>
<?php if (isset($event['Event']['published']) && 0 == $event['Event']['published'] && ($isAdmin || (isset($mayPublish) && $mayPublish))): ?>
<li><?php echo $this->Form->postLink('Publish Event', array('controller' => 'events', 'action' => 'alert', $event['Event']['id']), null, 'Are you sure this event is complete and everyone should be informed?'); ?></li>
<li><?php echo $this->Form->postLink('Publish (no email)', array('controller' => 'events', 'action' => 'publish', $event['Event']['id']), null, 'Publish but do NOT send alert email? Only for minor changes!'); ?></li>
<?php endif; ?>
<?php
$publishButtons = ' style="display:none;"';
$exportButtons = ' style="display:none;"';
if (isset($event['Event']['published']) && 0 == $event['Event']['published'] && ($isAdmin || (isset($mayPublish) && $mayPublish))) $publishButtons = "";
if (isset($event['Event']['published']) && $event['Event']['published']) $exportButtons = "";
?>
<li<?php echo $publishButtons; ?> class="publishButtons"><a href="#" onClick="publishPopup('<?php echo $event['Event']['id']; ?>', 'alert')">Publish Event</a></li>
<li<?php echo $publishButtons; ?> class="publishButtons"><a href="#" onClick="publishPopup('<?php echo $event['Event']['id']; ?>', 'publish')">Publish (no email)</a></li>
<li <?php if ($menuItem === 'contact') echo 'class="active"';?>><a href="/events/contact/<?php echo $event['Event']['id'];?>">Contact Reporter</a></li>
<li><a href="/events/xml/download/<?php echo $event['Event']['id'];?>">Download as XML</a></li>
<?php if (isset($event['Event']['published']) && $event['Event']['published']): ?>
<li><a href="/events/downloadOpenIOCEvent/<?php echo $event['Event']['id'];?>">Download as IOC</a></li>
<li><a href="/events/csv/download/<?php echo $event['Event']['id'];?>/1">Download as CSV</a></li>
<li><a href="/events/stix/download/<?php echo $event['Event']['id'];?>.xml">Download as STIX XML</a></li>
<li><a href="/events/stix/download/<?php echo $event['Event']['id'];?>.json">Download as STIX JSON</a></li>
<?php endif; ?>
<li<?php echo $exportButtons; ?> class="exportButtons"><a href="/events/downloadOpenIOCEvent/<?php echo $event['Event']['id'];?>">Download as IOC</a></li>
<li<?php echo $exportButtons; ?> class="exportButtons"><a href="/events/csv/download/<?php echo $event['Event']['id'];?>/1">Download as CSV</a></li>
<li<?php echo $exportButtons; ?> class="exportButtons"><a href="/events/stix/download/<?php echo $event['Event']['id'];?>.xml">Download as STIX XML</a></li>
<li<?php echo $exportButtons; ?> class="exportButtons"><a href="/events/stix/download/<?php echo $event['Event']['id'];?>.json">Download as STIX JSON</a></li>
<li class="divider"></li>
<li><a href="/events/index">List Events</a></li>
<?php if ($isAclAdd): ?>

View File

@ -0,0 +1,30 @@
<div class="confirmation">
<?php
echo $this->Form->create('Event', array('style' => 'margin:0px;', 'id' => 'PromptForm', 'url' => '/events/' . $type . '/' . $id));
$extraTitle = "";
if ($type == 'publish') $extraTitle = ' (no email)';
?>
<legend>Publish Event<?php echo $extraTitle; ?></legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<?php if ($type == 'alert'): ?>
<p>Are you sure this event is complete and everyone should be informed?</p>
<?php else: ?>
<p>Publish but do NOT send alert email? Only for minor changes!</p>
<?php endif; ?>
<table>
<tr>
<td style="vertical-align:top">
<span id="PromptYesButton" class="btn btn-primary" onClick="submitPublish()">Yes</span>
</td>
<td style="width:540px;">
</td>
<td style="vertical-align:top;">
<span class="btn btn-inverse" id="PromptNoButton" onClick="cancelPrompt();">No</span>
</td>
</tr>
</table>
</div>
<?php
echo $this->Form->end();
?>
</div>

View File

@ -0,0 +1,2 @@
<?php
echo json_encode($result);

View File

@ -102,18 +102,26 @@ $mayPublish = ($isAclPublish && $event['Event']['orgc'] == $me['org']);
<?php echo nl2br(h($event['Event']['info'])); ?>
&nbsp;
</dd>
<?php if ($isAclPublish && $event['Event']['published'] == 0) :?>
<dt class="visibleDL">Published</dt>
<dd class="visibleDL">
<?php echo ($event['Event']['published'] == 1 ? 'Yes' : 'No'); ?>
&nbsp;
</dd>
<?php else: ?>
<dt>Published</dt>
<dd style="color: <?php echo ($event['Event']['published'] == 1 ? 'green' : 'red'); ?>">
<b><?php echo ($event['Event']['published'] == 1 ? 'Yes' : 'No'); ?></b>
&nbsp;
</dd>
<?php
$published = '';
$notPublished = 'style="display:none;"';
if ($event['Event']['published'] == 0) {
$published = 'style="display:none;"';
$notPublished = '';
}
?>
<dt class="published" <?php echo $published;?>>Published</dt>
<dd class="published green" <?php echo $published;?>>Yes</dd>
<?php
if ($isAclPublish) :
?>
<dt class="visibleDL notPublished" <?php echo $notPublished;?>>Published</dt>
<dd class="visibleDL notPublished" <?php echo $notPublished;?>>No</dd>
<?php
else:
?>
<dt class="notPublished" <?php echo $notPublished;?>>Published</dt>
<dd class="notPublished red" <?php echo $notPublished;?>>No</dd>
<?php endif; ?>
</dl>
</div>

View File

@ -28,8 +28,10 @@ Please note that this XML page is a representation of the /events/index page.
Because the /events/index page is paginated you will have a limited number of results.
You can for example ask: /events/index/limit:999.xml to get the 999 first records.
(A maximum has been set to 9999)
You can also sort the table by using the sort and direction parameters. For example:
/events/index/sort:date/direction:desc.xml
To export all the events at once, with their attributes, use the export functionality.
-->

View File

@ -27,7 +27,16 @@
Type
</th>
<th>
<?php echo $this->Paginator->sort('id', 'Info');?>
<?php echo $this->Paginator->sort('id', 'Event Info');?>
</th>
<th>
<?php echo $this->Paginator->sort('value', 'Proposed value');?>
</th>
<th>
<?php echo $this->Paginator->sort('category', 'Category');?>
</th>
<th>
<?php echo $this->Paginator->sort('type', 'Type');?>
</th>
</tr>
<?php foreach ($shadowAttributes as $event):?>
@ -50,6 +59,15 @@
<td onclick="document.location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo h($event['Event']['info']); ?>
</td>
<td onclick="document.location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo h($event['ShadowAttribute']['value']);?>
</td>
<td class="short" onclick="document.location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo h($event['ShadowAttribute']['category']);?>
</td>
<td class="short" onclick="document.location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo h($event['ShadowAttribute']['type']);?>
</td>
</tr>
<?php endforeach; ?>
</table>

View File

@ -9,6 +9,21 @@ function deleteObject(type, action, id, event) {
});
}
function publishPopup(id, type) {
var action = "alert";
if (type == "publish") action = "publish";
var destination = 'attributes';
$.get( "/events/" + action + "/" + id, function(data) {
$("#confirmation_box").fadeIn();
$("#gray_out").fadeIn();
$("#confirmation_box").html(data);
});
}
function submitPublish(id, type) {
$("#PromptForm").submit();
}
function editTemplateElement(type, id) {
$.get( "/template_elements/edit/" + type + "/" + id, function(data) {
$("#popover_form").fadeIn();
@ -55,6 +70,7 @@ function acceptObject(type, id, event) {
data: formData,
success:function (data, textStatus) {
updateIndex(event, 'event');
eventUnpublish();
handleGenericAjaxResponse(data);
},
type:"post",
@ -63,6 +79,13 @@ function acceptObject(type, id, event) {
});
}
function eventUnpublish() {
$('.publishButtons').show();
$('.exportButtons').hide();
$('.published').hide();
$('.notPublished').show();
}
function updateIndex(id, context) {
var url, div;
if (context == 'event') {
@ -248,6 +271,7 @@ function handleAjaxEditResponse(data, name, type, id, field, event) {
showMessage('success', responseArray.success);
updateAttributeFieldOnSuccess(name, type, id, field, event);
updateAttributeFieldOnSuccess(name, type, id, 'timestamp', event);
eventUnpublish();
} else {
showMessage('fail', 'Validation failed: ' + responseArray.errors.value);
updateAttributeFieldOnSuccess(name, type, id, field, event);
@ -266,8 +290,10 @@ function handleGenericAjaxResponse(data) {
}
if (responseArray.saved) {
showMessage('success', responseArray.success);
return true;
} else {
showMessage('fail', responseArray.errors);
return false;
}
}
@ -304,7 +330,8 @@ function deleteSelectedAttributes(event) {
url:"/attributes/deleteSelected/" + event,
success:function (data, textStatus) {
updateIndex(event, 'event');
handleGenericAjaxResponse(data);
var result = handleGenericAjaxResponse(data);
if (result == true) eventUnpublish();
},
});
}
@ -436,7 +463,8 @@ function submitPopoverForm(context_id, referer, update_context_id) {
},
data: $("#submitButton").closest("form").serialize(),
success:function (data, textStatus) {
handleAjaxPopoverResponse(data, context_id, url, referer, context, contextNamingConvention);
var result = handleAjaxPopoverResponse(data, context_id, url, referer, context, contextNamingConvention);
if (context == 'event' && (referer == 'add' || referer == 'massEdit' || referer == 'replaceAttributes')) eventUnpublish();
$(".loading").show();
},
type:"post",