2013-04-25 14:04:08 +02:00
< ? php
App :: uses ( 'AppController' , 'Controller' );
App :: uses ( 'Folder' , 'Utility' );
App :: uses ( 'File' , 'Utility' );
/**
* ShadowAttributes Controller
*
* Handles requests to edit attributes , add attributes
*
* @ property ShadowAttribute $ShadowAttribute
*/
class ShadowAttributesController extends AppController {
2013-06-20 16:21:55 +02:00
public $components = array ( 'Acl' , 'Security' , 'RequestHandler' , 'Email' );
2013-04-25 14:04:08 +02:00
public $paginate = array (
'limit' => 60 ,
'maxLimit' => 9999 ,
);
public $helpers = array ( 'Js' => array ( 'Jquery' ));
public function beforeFilter () {
parent :: beforeFilter ();
$this -> Security -> validatePost = true ;
// convert uuid to id if present in the url, and overwrite id field
if ( isset ( $this -> params -> query [ 'uuid' ])) {
$params = array (
'conditions' => array ( 'ShadowAttribute.uuid' => $this -> params -> query [ 'uuid' ]),
'recursive' => 0 ,
'fields' => 'ShadowAttribute.id'
);
$result = $this -> ShadowAttribute -> find ( 'first' , $params );
if ( isset ( $result [ 'ShadowAttribute' ]) && isset ( $result [ 'ShadowAttribute' ][ 'id' ])) {
$id = $result [ 'ShadowAttribute' ][ 'id' ];
$this -> params -> addParams ( array ( 'pass' => array ( $id ))); // FIXME find better way to change id variable if uuid is found. params->url and params->here is not modified accordingly now
}
}
// if not admin or own org, check private as well..
2013-06-20 16:21:55 +02:00
if ( ! $this -> _isSiteAdmin ()) {
2013-04-25 14:04:08 +02:00
$this -> paginate = Set :: merge ( $this -> paginate , array (
'conditions' =>
array ( 'OR' =>
array (
'Event.org =' => $this -> Auth -> user ( 'org' ),
'AND' => array (
2013-06-11 13:51:11 +02:00
'ShadowAttribute.org =' => $this -> Auth -> user ( 'org' ),
'Event.distribution >' => 0 ,
2015-02-05 17:16:40 +01:00
Configure :: read ( 'MISP.unpublishedprivate' ) ? array ( 'Event.published =' => 1 ) : array (),
2013-06-11 13:51:11 +02:00
),
)
)));
2013-04-25 14:04:08 +02:00
}
}
2015-06-25 11:51:36 +02:00
private function __accept ( $id ) {
2013-04-25 14:04:08 +02:00
$this -> loadModel ( 'Attribute' );
2014-01-28 16:27:58 +01:00
$this -> Attribute -> Behaviors -> detach ( 'SysLogLogable.SysLogLogable' );
2015-10-28 23:32:30 +01:00
$shadow = $this -> ShadowAttribute -> find (
'first' ,
array (
'recursive' => - 1 ,
'conditions' => array (
'ShadowAttribute.id' => $id ,
'deleted' => false
),
)
);
if ( empty ( $shadow )) return array ( 'false' => true , 'errors' => 'Proposal not found.' );
$shadow = $shadow [ 'ShadowAttribute' ];
2013-04-25 14:04:08 +02:00
// If the old_id is set to anything but 0 then we're dealing with a proposed edit to an existing attribute
if ( $shadow [ 'old_id' ] != 0 ) {
// Find the live attribute by the shadow attribute's uuid, so we can begin editing it
2013-08-20 10:17:37 +02:00
$this -> Attribute -> contain = 'Event' ;
2015-10-28 23:32:30 +01:00
$activeAttribute = $this -> Attribute -> findByUuid ( $shadow [ 'uuid' ]);
2015-06-25 11:51:36 +02:00
2013-04-25 14:04:08 +02:00
// Send those away that shouldn't be able to see this
2013-06-20 16:21:55 +02:00
if ( ! $this -> _isSiteAdmin ()) {
2013-08-20 17:20:37 +02:00
if ( $activeAttribute [ 'Event' ][ 'orgc' ] != $this -> Auth -> user ( 'org' ) || ( ! $this -> userRole [ 'perm_modify' ])) {
2013-08-20 17:04:42 +02:00
$this -> Session -> setFlash ( 'You don\'t have permission to do that' );
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $this -> ShadowAttribute -> data [ 'ShadowAttribute' ][ 'event_id' ]));
2013-04-25 14:04:08 +02:00
}
}
// Update the live attribute with the shadow data
2015-06-25 11:51:36 +02:00
$fieldsToUpdate = array ( 'value1' , 'value2' , 'value' , 'type' , 'category' , 'comment' , 'to_ids' );
foreach ( $fieldsToUpdate as $f ) $activeAttribute [ 'Attribute' ][ $f ] = $shadow [ $f ];
2014-04-01 16:20:47 +02:00
$date = new DateTime ();
$activeAttribute [ 'Attribute' ][ 'timestamp' ] = $date -> getTimestamp ();
2013-04-25 14:04:08 +02:00
$this -> Attribute -> save ( $activeAttribute [ 'Attribute' ]);
2014-08-11 15:11:57 +02:00
$this -> ShadowAttribute -> setDeleted ( $id );
2013-04-25 14:04:08 +02:00
$this -> loadModel ( 'Event' );
2014-01-28 16:27:58 +01:00
$this -> Event -> Behaviors -> detach ( 'SysLogLogable.SysLogLogable' );
2013-04-25 14:04:08 +02:00
$this -> Event -> recursive = - 1 ;
2013-06-20 16:21:55 +02:00
// Unpublish the event, accepting a proposal is modifying the event after all. Also, reset the lock.
$event = $this -> Event -> read ( null , $activeAttribute [ 'Attribute' ][ 'event_id' ]);
2014-01-06 05:15:47 +01:00
$fieldList = array ( 'proposal_email_lock' , 'id' , 'info' , 'published' , 'timestamp' );
$event [ 'Event' ][ 'timestamp' ] = $date -> getTimestamp ();
2013-06-20 16:21:55 +02:00
$event [ 'Event' ][ 'proposal_email_lock' ] = 0 ;
$event [ 'Event' ][ 'published' ] = 0 ;
2014-05-02 14:29:15 +02:00
if ( $this -> Event -> save ( $event , array ( 'fieldList' => $fieldList ))) {
$this -> Log = ClassRegistry :: init ( 'Log' );
$this -> Log -> create ();
$this -> Log -> save ( array (
'org' => $this -> Auth -> user ( 'org' ),
'model' => 'ShadowAttribute' ,
'model_id' => $id ,
'email' => $this -> Auth -> user ( 'email' ),
'action' => 'accept' ,
'title' => 'Proposal (' . $shadow [ 'id' ] . ') of ' . $shadow [ 'org' ] . ' to Attribute (' . $shadow [ 'old_id' ] . ') of Event (' . $shadow [ 'event_id' ] . ') accepted - ' . $shadow [ 'category' ] . '/' . $shadow [ 'type' ] . ' ' . $shadow [ 'value' ],
2015-06-25 11:51:36 +02:00
));
return array ( 'saved' => true , 'success' => 'Proposed change accepted.' );
2014-05-02 14:29:15 +02:00
} else {
2015-06-25 11:51:36 +02:00
return array ( 'false' => true , 'errors' => 'Could not accept proposal.' );
2014-05-02 14:29:15 +02:00
}
2013-04-25 14:04:08 +02:00
} else {
// If the old_id is set to 0, then we're dealing with a brand new proposed attribute
// The idea is to load the event that the new attribute will be attached to, create an attribute to it and set the distribution equal to that of the event
$toDeleteId = $shadow [ 'id' ];
2013-06-20 16:21:55 +02:00
$this -> loadModel ( 'Event' );
2014-01-28 16:27:58 +01:00
$this -> Event -> Behaviors -> detach ( 'SysLogLogable.SysLogLogable' );
2013-04-25 14:04:08 +02:00
$this -> Event -> recursive = - 1 ;
2013-06-20 16:21:55 +02:00
$event = $this -> Event -> read ( null , $shadow [ 'event_id' ]);
2015-06-25 11:51:36 +02:00
2013-08-20 17:04:42 +02:00
if ( ! $this -> _isSiteAdmin ()) {
2014-01-13 16:38:49 +01:00
if (( $event [ 'Event' ][ 'orgc' ] != $this -> Auth -> user ( 'org' )) || ( ! $this -> userRole [ 'perm_modify' ])) {
2013-08-20 17:04:42 +02:00
$this -> Session -> setFlash ( 'You don\'t have permission to do that' );
2013-08-20 17:20:37 +02:00
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'index' ));
2013-08-20 17:04:42 +02:00
}
}
2014-01-28 16:27:58 +01:00
$shadowForLog = $shadow ;
2013-08-20 17:04:42 +02:00
// Stuff that we won't use in its current form for the attribute
unset ( $shadow [ 'email' ], $shadow [ 'org' ], $shadow [ 'id' ], $shadow [ 'old_id' ]);
2015-06-25 11:51:36 +02:00
2013-04-25 14:04:08 +02:00
$attribute = $shadow ;
2015-06-25 11:51:36 +02:00
2013-04-25 14:04:08 +02:00
// set the distribution equal to that of the event
2013-06-20 16:21:55 +02:00
$attribute [ 'distribution' ] = $event [ 'Event' ][ 'distribution' ];
2013-04-25 14:04:08 +02:00
$this -> Attribute -> create ();
$this -> Attribute -> save ( $attribute );
if ( $this -> ShadowAttribute -> typeIsAttachment ( $shadow [ 'type' ])) {
$this -> _moveFile ( $toDeleteId , $this -> Attribute -> id , $shadow [ 'event_id' ]);
}
2014-08-11 15:11:57 +02:00
$this -> ShadowAttribute -> setDeleted ( $toDeleteId );
2015-06-25 11:51:36 +02:00
2013-06-20 16:21:55 +02:00
$fieldList = array ( 'proposal_email_lock' , 'id' , 'info' , 'published' );
2014-08-14 11:33:33 +02:00
if ( $this -> Auth -> user ( 'org' ) == $event [ 'Event' ][ 'orgc' ]) {
$event [ 'Event' ][ 'proposal_email_lock' ] = 0 ;
}
2013-06-20 16:21:55 +02:00
$event [ 'Event' ][ 'published' ] = 0 ;
2014-12-10 14:08:08 +01:00
$date = new DateTime ();
$event [ 'Event' ][ 'timestamp' ] = $date -> getTimestamp ();
2014-05-02 14:29:15 +02:00
if ( $this -> Event -> save ( $event , array ( 'fieldList' => $fieldList ))) {
$this -> Log = ClassRegistry :: init ( 'Log' );
$this -> Log -> create ();
$this -> Log -> save ( array (
2015-06-25 11:51:36 +02:00
'org' => $this -> Auth -> user ( 'org' ),
'model' => 'ShadowAttribute' ,
'model_id' => $id ,
'email' => $this -> Auth -> user ( 'email' ),
'action' => 'accept' ,
'title' => 'Proposal (' . $shadowForLog [ 'id' ] . ') of ' . $shadowForLog [ 'org' ] . ' to Event(' . $shadowForLog [ 'event_id' ] . ') accepted' ,
'change' => null ,
2014-05-02 14:29:15 +02:00
));
2015-06-25 11:51:36 +02:00
return array ( 'saved' => true , 'success' => 'Proposal accepted.' );
2014-05-02 14:29:15 +02:00
} else {
2015-06-25 11:51:36 +02:00
return array ( 'false' => true , 'errors' => 'Could not accept proposal.' );
2014-05-02 14:29:15 +02:00
}
2013-04-25 14:04:08 +02:00
}
}
2015-06-25 11:51:36 +02:00
/**
* accept method
*
* @ return void
*
*/
// Accept a proposed edit and update the attribute
public function accept ( $id = null ) {
if ( ! $this -> request -> is ( 'post' )) {
throw new MethodNotAllowedException ();
}
2015-10-28 23:32:30 +01:00
$response = $this -> __accept ( $id );
2015-06-25 11:51:36 +02:00
if ( $this -> _isRest ()) {
2015-10-28 23:32:30 +01:00
if ( isset ( $response [ 'success' ])) {
$this -> set ( 'name' , $response [ 'success' ]);
$this -> set ( 'message' , $response [ 'success' ]);
$this -> set ( 'url' , '/shadow_attributes/accept/' . $id );
$this -> set ( '_serialize' , array ( 'name' , 'message' , 'url' ));
} else {
throw new MethodNotAllowedException ( $response [ 'errors' ]);
}
} else {
$this -> autoRender = false ;
return new CakeResponse ( array ( 'body' => json_encode ( $response ), 'status' => 200 ));
2015-06-25 11:51:36 +02:00
}
}
2013-04-25 14:04:08 +02:00
// If we accept a proposed attachment, then the attachment itself needs to be moved from files/eventId/shadow/shadowId to files/eventId/attributeId
private function _moveFile ( $shadowId , $newId , $eventId ){
$pathOld = APP . " files " . DS . $eventId . DS . " shadow " . DS . $shadowId ;
$pathNew = APP . " files " . DS . $eventId . DS . $newId ;
if ( rename ( $pathOld , $pathNew )) {
return true ;
} else {
$this -> Session -> setFlash ( __ ( 'Moving of the file that this attachment references failed.' , true ), 'default' , array ());
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $eventId ));
}
}
2015-06-25 11:51:36 +02:00
private function __discard ( $id ) {
2015-10-28 23:32:30 +01:00
$sa = $this -> ShadowAttribute -> find (
'first' ,
array (
'recursive' => - 1 ,
'conditions' => array (
'ShadowAttribute.id' => $id ,
'deleted' => false
),
)
);
if ( empty ( $sa )) return false ;
$eventId = $sa [ 'ShadowAttribute' ][ 'event_id' ];
2014-05-09 23:12:51 +02:00
$this -> loadModel ( 'Event' );
$this -> Event -> Behaviors -> detach ( 'SysLogLogable.SysLogLogable' );
$this -> Event -> recursive = - 1 ;
$this -> Event -> id = $eventId ;
$this -> Event -> read ();
// Send those away that shouldn't be able to see this
if ( ! $this -> _isSiteAdmin ()) {
2015-10-28 23:32:30 +01:00
if ((( $this -> Event -> data [ 'Event' ][ 'orgc' ] != $this -> Auth -> user ( 'org' )) && ( $this -> Auth -> user ( 'org' ) != $sa [ 'ShadowAttribute' ][ 'org' ])) || ( ! $this -> userRole [ 'perm_modify' ])) {
2015-06-25 11:51:36 +02:00
return false ;
2014-05-09 23:12:51 +02:00
}
}
2014-08-11 15:11:57 +02:00
if ( $this -> ShadowAttribute -> setDeleted ( $id )) {
2014-08-14 11:33:33 +02:00
if ( $this -> Auth -> user ( 'org' ) == $this -> Event -> data [ 'Event' ][ 'orgc' ]) {
$this -> _setProposalLock ( $eventId , false );
}
2014-10-15 15:39:28 +02:00
$this -> Log = ClassRegistry :: init ( 'Log' );
$this -> Log -> create ();
$this -> Log -> save ( array (
'org' => $this -> Auth -> user ( 'org' ),
'model' => 'ShadowAttribute' ,
'model_id' => $id ,
'email' => $this -> Auth -> user ( 'email' ),
'action' => 'discard' ,
'title' => 'Proposal (' . $sa [ 'ShadowAttribute' ][ 'id' ] . ') of ' . $sa [ 'ShadowAttribute' ][ 'org' ] . ' discarded - ' . $sa [ 'ShadowAttribute' ][ 'category' ] . '/' . $sa [ 'ShadowAttribute' ][ 'type' ] . ' ' . $sa [ 'ShadowAttribute' ][ 'value' ],
));
2015-06-25 11:51:36 +02:00
return true ;
}
return false ;
}
/**
* discard method
*
* @ return void
*
*/
// This method will discard a proposed change. Users that can delete the proposals are the publishing users of the org that created the event and of the ones that created the proposal - in addition to site admins of course
public function discard ( $id = null ) {
if ( $this -> request -> is ( 'post' )) {
if ( $this -> __discard ( $id )) {
2015-10-28 23:32:30 +01:00
if ( $this -> _isRest ()) {
$this -> set ( 'name' , 'Proposal discarded.' );
$this -> set ( 'message' , 'Proposal discarded.' );
$this -> set ( 'url' , '/shadow_attributes/discard/' . $id );
$this -> set ( '_serialize' , array ( 'name' , 'message' , 'url' ));
} else {
$this -> autoRender = false ;
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => true , 'success' => 'Proposal discarded.' )), 'status' => 200 ));
}
2014-05-09 23:12:51 +02:00
} else {
2015-10-28 23:32:30 +01:00
if ( $this -> _isRest ()) {
throw new MethodNotAllowedException ( 'Could not discard proposal.' );
} else {
$this -> autoRender = false ;
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'false' => true , 'errors' => 'Could not discard proposal.' )), 'status' => 200 ));
}
2013-04-25 14:04:08 +02:00
}
2014-05-02 14:29:15 +02:00
} else {
2015-10-28 23:32:30 +01:00
if ( ! $this -> request -> is ( 'ajax' )) {
throw new MethodNotAllowedException ();
}
$this -> autoRender = false ;
2014-05-09 23:12:51 +02:00
$this -> set ( 'id' , $id );
$shadowAttribute = $this -> ShadowAttribute -> find ( 'first' , array (
'conditions' => array ( 'id' => $id ),
'recursive' => - 1 ,
'fields' => array ( 'id' , 'event_id' ),
));
$this -> set ( 'event_id' , $shadowAttribute [ 'ShadowAttribute' ][ 'event_id' ]);
$this -> render ( 'ajax/shadowAttributeConfirmationForm' );
2014-05-02 14:29:15 +02:00
}
2013-04-25 14:04:08 +02:00
}
/**
* add method
*
* @ return void
*
* @ throws NotFoundException // TODO Exception
*/
public function add ( $eventId = null ) {
2014-05-02 14:29:15 +02:00
if ( $this -> request -> is ( 'ajax' )) {
$this -> set ( 'ajax' , true );
//$this->autoRender = false;
$this -> layout = 'ajax' ;
}
else $this -> set ( 'ajax' , false );
2015-10-28 23:32:30 +01:00
// If the user is not a site admin, have the following requirement for proposing attributes:
// - The event has to either be owned by the user's ORG
// - or it has to have a distribution higher than 0
// - but at the same time it should not be an event created by the user's org
$distConditions = array ();
if ( ! $this -> _isSiteAdmin ()) {
$distConditions = array (
'AND' => array (
'OR' => array (
'Event.distribution >' => 0 ,
'Event.org' => $this -> Auth -> user ( 'org' ),
),
'Event.orgc !=' => $this -> Auth -> user ( 'org' ),
),
);
}
2014-01-10 14:56:21 +01:00
$event = $this -> ShadowAttribute -> Event -> find ( 'first' , array (
2015-10-28 23:32:30 +01:00
'conditions' => array ( 'AND' => array ( 'Event.id' => $eventId , $distConditions )),
2014-01-10 14:56:21 +01:00
'recursive' => - 1 ,
'fields' => array ( 'id' , 'orgc' , 'distribution' , 'org' ),
));
2015-10-28 23:32:30 +01:00
if ( empty ( $event )) throw new NotFoundException ( 'Invalid Event' );
2013-04-25 14:04:08 +02:00
if ( $this -> request -> is ( 'post' )) {
2015-10-28 23:32:30 +01:00
if ( isset ( $this -> request -> data [ 'request' ])) $this -> request -> data = $this -> request -> data [ 'request' ];
// rearrange the request in case someone didn't RTFM
$invalidNames = array ( 'Attribute' , 'Proposal' );
foreach ( $invalidNames as & $iN ) if ( isset ( $this -> request -> data [ $iN ]) && ! isset ( $this -> request -> data [ 'ShadowAttribute' ])) $this -> request -> data [ 'ShadowAttribute' ] = $this -> request -> data [ $iN ];
2014-05-02 14:29:15 +02:00
if ( $this -> request -> is ( 'ajax' )) $this -> autoRender = false ;
2013-04-25 14:04:08 +02:00
// Give error if someone tried to submit a attribute with attachment or malware-sample type.
// TODO change behavior attachment options - this is bad ... it should rather by a messagebox or should be filtered out on the view level
if ( isset ( $this -> request -> data [ 'ShadowAttribute' ][ 'type' ]) && $this -> ShadowAttribute -> typeIsAttachment ( $this -> request -> data [ 'ShadowAttribute' ][ 'type' ])) {
$this -> Session -> setFlash ( __ ( 'Attribute has not been added: attachments are added by "Add attachment" button' , true ), 'default' , array (), 'error' );
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ]));
}
2015-10-28 23:32:30 +01:00
if ( isset ( $eventId )) $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ] = $eventId ;
2014-01-05 21:30:39 +01:00
$temp = $this -> _getEventData ( $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ]);
$event_uuid = $temp [ 'uuid' ];
$event_org = $temp [ 'orgc' ];
2013-04-25 14:04:08 +02:00
//
// multiple attributes in batch import
//
2015-10-28 23:32:30 +01:00
if ( ! $this -> _isRest () && ( isset ( $this -> request -> data [ 'ShadowAttribute' ][ 'batch_import' ]) && $this -> request -> data [ 'ShadowAttribute' ][ 'batch_import' ] == 1 )) {
2013-04-25 14:04:08 +02:00
// make array from value field
$attributes = explode ( " \n " , $this -> request -> data [ 'ShadowAttribute' ][ 'value' ]);
$fails = " " ; // will be used to keep a list of the lines that failed or succeeded
$successes = " " ;
// TODO loop-holes,
// the value null value thing
foreach ( $attributes as $key => $attribute ) {
$attribute = trim ( $attribute );
if ( strlen ( $attribute ) == 0 )
continue ; // don't do anything for empty lines
$this -> ShadowAttribute -> create ();
$this -> request -> data [ 'ShadowAttribute' ][ 'value' ] = $attribute ; // set the value as the content of the single line
$this -> request -> data [ 'ShadowAttribute' ][ 'email' ] = $this -> Auth -> user ( 'email' );
$this -> request -> data [ 'ShadowAttribute' ][ 'org' ] = $this -> Auth -> user ( 'org' );
2014-01-05 21:30:39 +01:00
$this -> request -> data [ 'ShadowAttribute' ][ 'event_uuid' ] = $event_uuid ;
$this -> request -> data [ 'ShadowAttribute' ][ 'event_org' ] = $event_org ;
2013-04-25 14:04:08 +02:00
// TODO loop-holes,
// there seems to be a loop-hole in misp here
// be it an create and not an update
$this -> ShadowAttribute -> id = null ;
if ( $this -> ShadowAttribute -> save ( $this -> request -> data )) {
$successes .= " " . ( $key + 1 );
} else {
$fails .= " " . ( $key + 1 );
}
}
// we added all the attributes,
2014-05-02 14:29:15 +02:00
if ( $this -> request -> is ( 'ajax' )) {
// handle it if some of them failed!!!!
if ( $fails ) {
$error_message = 'The lines' . $fails . ' could not be saved. Please, try again.' ;
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => false , 'errors' => $error_message )), 'status' => 200 ));
2013-04-25 14:04:08 +02:00
} else {
2014-05-02 14:29:15 +02:00
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => true )), 'status' => 200 ));
}
} else {
if ( $fails ) {
// list the ones that failed
if ( ! CakeSession :: read ( 'Message.flash' )) {
$this -> Session -> setFlash ( __ ( 'The lines' . $fails . ' could not be saved. Please, try again.' , true ), 'default' , array (), 'error' );
} else {
$existingFlash = CakeSession :: read ( 'Message.flash' );
$this -> Session -> setFlash ( __ ( 'The lines' . $fails . ' could not be saved. ' . $existingFlash [ 'message' ], true ), 'default' , array (), 'error' );
}
}
if ( $successes ) {
// list the ones that succeeded
2014-08-18 13:30:18 +02:00
$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 ));
2013-04-25 14:04:08 +02:00
}
}
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ]));
} else {
//
// single attribute
//
// create the attribute
$this -> ShadowAttribute -> create ();
$savedId = $this -> ShadowAttribute -> getId ();
$this -> request -> data [ 'ShadowAttribute' ][ 'email' ] = $this -> Auth -> user ( 'email' );
$this -> request -> data [ 'ShadowAttribute' ][ 'org' ] = $this -> Auth -> user ( 'org' );
2014-01-05 21:30:39 +01:00
$this -> request -> data [ 'ShadowAttribute' ][ 'event_uuid' ] = $event_uuid ;
$this -> request -> data [ 'ShadowAttribute' ][ 'event_org' ] = $event_org ;
2013-04-25 14:04:08 +02:00
if ( $this -> ShadowAttribute -> save ( $this -> request -> data )) {
2014-08-18 13:30:18 +02:00
// 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. " ;
}
2013-04-25 14:04:08 +02:00
// inform the user and redirect
2014-05-02 14:29:15 +02:00
if ( $this -> request -> is ( 'ajax' )) {
$this -> autoRender = false ;
2014-08-18 13:30:18 +02:00
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => true , 'success' => 'Proposal added' . $emailResult )), 'status' => 200 ));
2015-10-28 23:32:30 +01:00
} else if ( $this -> _isRest ()) {
$sa = $this -> ShadowAttribute -> find (
'first' ,
array (
'conditions' => array ( 'ShadowAttribute.id' => $this -> ShadowAttribute -> id ),
'recursive' => - 1 ,
'fields' => array ( 'id' , 'old_id' , 'event_id' , 'type' , 'category' , 'value' , 'comment' , 'to_ids' , 'uuid' , 'event_org' , 'email' , 'deleted' , 'timestamp' )
)
);
$this -> set ( 'ShadowAttribute' , $sa [ 'ShadowAttribute' ]);
$this -> set ( '_serialize' , array ( 'ShadowAttribute' ));
2014-05-02 14:29:15 +02:00
} else {
$this -> Session -> setFlash ( __ ( 'The proposal has been saved' ));
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ]));
}
2013-04-25 14:04:08 +02:00
} else {
2014-05-02 14:29:15 +02:00
if ( $this -> request -> is ( 'ajax' )) {
$this -> autoRender = false ;
2015-10-28 23:32:30 +01:00
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => false , 'errors' => $this -> ShadowAttribute -> validationErrors )), 'status' => 200 ));
} else if ( $this -> _isRest ()) {
$message = '' ;
foreach ( $this -> ShadowAttribute -> validationErrors as $k => $v ) {
$message .= '[' . $k . ']: ' . $v [ 0 ] . PHP_EOL ;
}
throw new NotFoundException ( 'Could not save the proposal. Errors: ' . $message );
2014-05-02 14:29:15 +02:00
} else {
if ( ! CakeSession :: read ( 'Message.flash' )) {
$this -> Session -> setFlash ( __ ( 'The attribute could not be saved. Please, try again.' ));
}
2013-04-25 14:04:08 +02:00
}
}
}
} else {
// set the event_id in the form
$this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ] = $eventId ;
}
2014-05-02 14:29:15 +02:00
$this -> set ( 'event_id' , $eventId );
2013-04-25 14:04:08 +02:00
// combobox for types
$types = array_keys ( $this -> ShadowAttribute -> typeDefinitions );
$types = $this -> _arrayToValuesIndexArray ( $types );
$this -> set ( 'types' , $types );
// combobos for categories
$categories = $this -> ShadowAttribute -> validate [ 'category' ][ 'rule' ][ 1 ];
array_pop ( $categories );
$categories = $this -> _arrayToValuesIndexArray ( $categories );
$this -> set ( 'categories' , compact ( 'categories' ));
// combobox for distribution
$count = 0 ;
$this -> set ( 'typeDefinitions' , $this -> ShadowAttribute -> typeDefinitions );
$this -> set ( 'categoryDefinitions' , $this -> ShadowAttribute -> categoryDefinitions );
}
public function download ( $id = null ) {
$this -> ShadowAttribute -> id = $id ;
if ( ! $this -> ShadowAttribute -> exists ()) {
2015-10-21 23:44:07 +02:00
throw new NotFoundException ( __ ( 'Invalid Proposal' ));
2013-04-25 14:04:08 +02:00
}
2015-10-21 23:44:07 +02:00
$sa = $this -> ShadowAttribute -> find ( 'first' , array (
'recursive' => - 1 ,
'contain' => array ( 'Event' => array ( 'fields' => array ( 'Event.org' , 'Event.distribution' , 'Event.id' ))),
'conditions' => array ( 'ShadowAttribute.id' => $id )
));
if ( ! $this -> _isSiteAdmin () &&
$this -> Auth -> user ( 'org' ) !=
$sa [ 'Event' ][ 'org' ] &&
$sa [ 'Event' ][ 'distribution' ] == 0 ) {
throw new UnauthorizedException ( 'You do not have the permission to view this event.' );
}
$this -> __downloadAttachment ( $sa [ 'ShadowAttribute' ]);
}
private function __downloadAttachment ( $shadowAttribute ) {
2015-10-22 09:51:31 +02:00
$path = " files " . DS . 'shadow' . DS . $shadowAttribute [ 'event_id' ] . DS ;
2015-10-21 23:44:07 +02:00
$file = $shadowAttribute [ 'id' ];
2013-04-25 14:04:08 +02:00
$filename = '' ;
2015-10-21 23:44:07 +02:00
if ( 'attachment' == $shadowAttribute [ 'type' ]) {
$filename = $shadowAttribute [ 'value' ];
2013-04-25 14:04:08 +02:00
$fileExt = pathinfo ( $filename , PATHINFO_EXTENSION );
$filename = substr ( $filename , 0 , strlen ( $filename ) - strlen ( $fileExt ) - 1 );
2015-10-21 23:44:07 +02:00
} elseif ( 'malware-sample' == $shadowAttribute [ 'type' ]) {
$filenameHash = explode ( '|' , $shadowAttribute [ 'value' ]);
2013-04-25 14:04:08 +02:00
$filename = $filenameHash [ 0 ];
$filename = substr ( $filenameHash [ 0 ], strrpos ( $filenameHash [ 0 ], '\\' ));
$fileExt = " zip " ;
} else {
2015-10-21 23:44:07 +02:00
throw new NotFoundException ( __ ( 'Proposal not an attachment or malware-sample' ));
2013-04-25 14:04:08 +02:00
}
2013-07-10 17:31:18 +02:00
$this -> autoRender = false ;
$this -> response -> type ( $fileExt );
$this -> response -> file ( $path . $file , array ( 'download' => true , 'name' => $filename . '.' . $fileExt ));
2013-04-25 14:04:08 +02:00
}
/**
* add_attachment method
*
* @ return void
* @ throws InternalErrorException
*/
public function add_attachment ( $eventId = null ) {
2015-10-28 23:32:30 +01:00
$distConditions = array ();
if ( ! $this -> _isSiteAdmin ()) {
$distConditions = array (
'AND' => array (
'OR' => array (
'Event.distribution >' => 0 ,
'Event.org' => $this -> Auth -> user ( 'org' ),
),
'Event.orgc !=' => $this -> Auth -> user ( 'org' ),
),
);
}
2014-01-10 14:56:21 +01:00
$event = $this -> ShadowAttribute -> Event -> find ( 'first' , array (
2015-10-28 23:32:30 +01:00
'conditions' => array ( 'AND' => array ( 'Event.id' => $eventId , $distConditions )),
2014-01-10 14:56:21 +01:00
'recursive' => - 1 ,
2015-10-28 23:32:30 +01:00
'fields' => array ( 'id' , 'orgc' , 'distribution' , 'org' , 'uuid' ),
2014-01-10 14:56:21 +01:00
));
2015-10-28 23:32:30 +01:00
if ( empty ( $event )) throw new NotFoundException ( 'Invalid Event' );
2013-04-25 14:04:08 +02:00
if ( $this -> request -> is ( 'post' )) {
// Check if there were problems with the file upload
// only keep the last part of the filename, this should prevent directory attacks
if (( isset ( $this -> request -> data [ 'ShadowAttribute' ][ 'value' ][ 'error' ]) && $this -> request -> data [ 'ShadowAttribute' ][ 'value' ][ 'error' ] == 0 ) ||
2015-10-21 23:44:07 +02:00
( ! empty ( $this -> request -> data [ 'ShadowAttribute' ][ 'value' ][ 'tmp_name' ]) && $this -> request -> data [ 'ShadowAttribute' ][ 'value' ][ 'tmp_name' ] != 'none' )
2013-04-25 14:04:08 +02:00
) {
if ( ! is_uploaded_file ( $tmpfile -> path ))
throw new InternalErrorException ( 'PHP says file was not uploaded. Are you attacking me?' );
} else {
$this -> Session -> setFlash ( __ ( 'There was a problem to upload the file.' , true ), 'default' , array (), 'error' );
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ]));
}
2015-10-21 23:44:07 +02:00
$fails = array ();
$completeFail = false ;
$filename = basename ( $this -> request -> data [ 'ShadowAttribute' ][ 'value' ][ 'name' ]);
$tmpfile = new File ( $this -> request -> data [ 'ShadowAttribute' ][ 'value' ][ 'tmp_name' ]);
$hashes = array ( 'md5' => 'malware-sample' , 'sha1' => 'filename|sha1' , 'sha256' => 'filename|sha256' );
2013-04-25 14:04:08 +02:00
if ( $this -> request -> data [ 'ShadowAttribute' ][ 'malware' ]) {
2015-10-21 23:44:07 +02:00
$result = $this -> Event -> Attribute -> handleMaliciousBase64 ( $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ], $filename , base64_encode ( $tmpfile -> read ()), array_keys ( $hashes ));
if ( ! $result [ 'success' ]) {
$this -> Session -> setFlash ( __ ( 'There was a problem to upload the file.' , true ), 'default' , array (), 'error' );
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ]));
}
foreach ( $hashes as $hash => $typeName ) {
if ( ! $result [ $hash ]) continue ;
$shadowAttribute = array (
'ShadowAttribute' => array (
'value' => $filename . '|' . $result [ $hash ],
'category' => $this -> request -> data [ 'ShadowAttribute' ][ 'category' ],
'type' => $typeName ,
'event_id' => $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ],
'to_ids' => 1 ,
'email' => $this -> Auth -> user ( 'email' ),
'org' => $this -> Auth -> user ( 'org' ),
2015-10-28 23:32:30 +01:00
'event_uuid' => $event [ 'Event' ][ 'uuid' ],
'event_org' => $event [ 'Event' ][ 'orgc' ],
2015-10-21 23:44:07 +02:00
)
);
if ( $hash == 'md5' ) $shadowAttribute [ 'ShadowAttribute' ][ 'data' ] = $result [ 'data' ];
$this -> ShadowAttribute -> create ();
$r = $this -> ShadowAttribute -> save ( $shadowAttribute );
if ( $r == false ) $fails [] = array ( $typeName );
if ( count ( $fails ) == count ( $hashes )) $completeFail = true ;
}
2013-04-25 14:04:08 +02:00
} else {
2015-10-21 23:44:07 +02:00
$shadowAttribute = array (
'ShadowAttribute' => array (
'value' => $filename ,
'category' => $this -> request -> data [ 'ShadowAttribute' ][ 'category' ],
'type' => 'attachment' ,
'event_id' => $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ],
'data' => base64_encode ( $tmpfile -> read ()),
'to_ids' => 0 ,
'email' => $this -> Auth -> user ( 'email' ),
'org' => $this -> Auth -> user ( 'org' ),
2015-10-28 23:32:30 +01:00
'event_uuid' => $event [ 'Event' ][ 'uuid' ],
'event_org' => $event [ 'Event' ][ 'orgc' ],
2015-10-21 23:44:07 +02:00
)
);
$this -> ShadowAttribute -> create ();
$r = $this -> ShadowAttribute -> save ( $shadowAttribute );
if ( $r == false ) {
$fails [] = array ( 'attachment' );
$completeFail = true ;
}
2013-04-25 14:04:08 +02:00
}
2015-10-21 23:44:07 +02:00
if ( ! $completeFail ) {
if ( ! $this -> __sendProposalAlertEmail ( $eventId )) $emailResult = " but sending out the alert e-mails has failed for at least one recipient. " ;
if ( empty ( $fails )) $this -> Session -> setFlash ( __ ( 'The attachment has been uploaded' ));
else $this -> Session -> setFlash ( __ ( 'The attachment has been uploaded, but some of the proposals could not be created. The failed proposals are: ' . implode ( ', ' , $fails )));
2013-04-25 14:04:08 +02:00
} else {
2015-10-21 23:44:07 +02:00
$this -> Session -> setFlash ( __ ( 'The attachment could not be saved, please contact your administrator.' ));
2013-04-25 14:04:08 +02:00
}
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ]));
} else {
// set the event_id in the form
$this -> request -> data [ 'ShadowAttribute' ][ 'event_id' ] = $eventId ;
}
// combobox for categories
$categories = $this -> ShadowAttribute -> validate [ 'category' ][ 'rule' ][ 1 ];
// just get them with attachments..
$selectedCategories = array ();
foreach ( $categories as $category ) {
if ( isset ( $this -> ShadowAttribute -> categoryDefinitions [ $category ])) {
$types = $this -> ShadowAttribute -> categoryDefinitions [ $category ][ 'types' ];
$alreadySet = false ;
foreach ( $types as $type ) {
if ( $this -> ShadowAttribute -> typeIsAttachment ( $type ) && ! $alreadySet ) {
// add to the whole..
$selectedCategories [] = $category ;
$alreadySet = true ;
continue ;
}
}
}
};
$categories = $this -> _arrayToValuesIndexArray ( $selectedCategories );
$this -> set ( 'categories' , $categories );
$this -> set ( 'attrDescriptions' , $this -> ShadowAttribute -> fieldDescriptions );
$this -> set ( 'typeDefinitions' , $this -> ShadowAttribute -> typeDefinitions );
$this -> set ( 'categoryDefinitions' , $this -> ShadowAttribute -> categoryDefinitions );
$this -> set ( 'zippedDefinitions' , $this -> ShadowAttribute -> zippedDefinitions );
$this -> set ( 'uploadDefinitions' , $this -> ShadowAttribute -> uploadDefinitions );
}
/**
* edit method
*
* @ param string $id
* @ return void
* @ throws NotFoundException
*/
// Propose an edit to an attribute
2015-10-28 23:32:30 +01:00
// Fields that can be used to edit an attribute when using the API:
// type, category, value, comment, to_ids
// if any of these fields is set, it will create a proposal
2013-04-25 14:04:08 +02:00
public function edit ( $id = null ) {
2015-10-28 23:32:30 +01:00
$distConditions = array ();
// If the attribute's distribution is private and the user is not the owner of the event or if the user is of the original creator org -> exception
// The owner should be able to create a shadow attribute, since a pushed community event would be private and tied to a single organisation on a synced instance
// The users of that organisation can only view but not edit the event, but they should be able to propose a change
2013-06-20 16:21:55 +02:00
if ( ! $this -> _isSiteAdmin ()) {
2015-10-28 23:32:30 +01:00
$distConditions = array (
'AND' => array (
'OR' => array (
'AND' => array (
'Event.distribution >' => 0 ,
'Attribute.distribution >' => 0 ,
),
'Event.org' => $this -> Auth -> user ( 'org' ),
),
'Event.orgc !=' => $this -> Auth -> user ( 'org' ),
),
);
2013-04-25 14:04:08 +02:00
}
2015-10-28 23:32:30 +01:00
$existingAttribute = $this -> ShadowAttribute -> Event -> Attribute -> find ( 'first' , array (
'recursive' => - 1 ,
'contain' => array ( 'Event' => array ( 'fields' => array ( 'Event.id' , 'Event.orgc' , 'Event.org' , 'Event.distribution' , 'Event.uuid' ))),
'conditions' => array ( 'AND' => array ( 'Attribute.id' => $id , $distConditions ))
));
if ( empty ( $existingAttribute )) throw new MethodNotAllowedException ( 'Invalid Attribute.' );
2013-04-25 14:04:08 +02:00
// Check if the attribute is an attachment, if yes, block the type and the value fields from being edited.
2015-10-28 23:32:30 +01:00
if ( 'attachment' == $existingAttribute [ 'Attribute' ][ 'type' ] || 'malware-sample' == $existingAttribute [ 'Attribute' ][ 'type' ] ) {
2013-04-25 14:04:08 +02:00
$this -> set ( 'attachment' , true );
$attachment = true ;
} else {
$this -> set ( 'attachment' , false );
$attachment = false ;
}
if ( $this -> request -> is ( 'post' ) || $this -> request -> is ( 'put' )) {
2015-10-28 23:32:30 +01:00
if ( isset ( $this -> request -> data [ 'request' ])) $this -> request -> data = $this -> request -> data [ 'request' ];
// rearrange the request in case someone didn't RTFM
$invalidNames = array ( 'Attribute' , 'Proposal' );
foreach ( $invalidNames as & $iN ) if ( isset ( $this -> request -> data [ $iN ]) && ! isset ( $this -> request -> data [ 'ShadowAttribute' ])) $this -> request -> data [ 'ShadowAttribute' ] = $this -> request -> data [ $iN ];
if ( $attachment ) {
$fields = array (
'static' => array ( 'old_id' => 'Attribute.id' , 'uuid' => 'Attribute.uuid' , 'event_id' => 'Attribute.event_id' , 'event_uuid' => 'Event.uuid' , 'event_org' => 'Event.orgc' , 'category' => 'Attribute.category' , 'type' => 'Attribute.type' ),
'optional' => array ( 'value' , 'to_ids' , 'comment' )
);
} else {
$fields = array (
'static' => array ( 'old_id' => 'Attribute.id' , 'uuid' => 'Attribute.uuid' , 'event_id' => 'Attribute.event_id' , 'event_uuid' => 'Event.uuid' , 'event_org' => 'Event.orgc' ),
'optional' => array ( 'category' , 'type' , 'value' , 'to_ids' , 'comment' )
);
}
foreach ( $fields [ 'static' ] as $k => $v ) {
$v = explode ( '.' , $v );
$this -> request -> data [ 'ShadowAttribute' ][ $k ] = $existingAttribute [ $v [ 0 ]][ $v [ 1 ]];
}
$validChangeMade = false ;
foreach ( $fields [ 'optional' ] as $v ) {
if ( ! isset ( $this -> request -> data [ 'ShadowAttribute' ][ $v ])) {
$this -> request -> data [ 'ShadowAttribute' ][ $v ] = $existingAttribute [ 'Attribute' ][ $v ];
} else {
$validChangeMade = true ;
}
}
if ( ! $validChangeMade ) throw new MethodNotAllowedException ( 'Invalid input.' );
2013-04-25 14:04:08 +02:00
$this -> request -> data [ 'ShadowAttribute' ][ 'org' ] = $this -> Auth -> user ( 'org' );
$this -> request -> data [ 'ShadowAttribute' ][ 'email' ] = $this -> Auth -> user ( 'email' );
if ( $this -> ShadowAttribute -> save ( $this -> request -> data )) {
2014-08-18 13:30:18 +02:00
$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. " ;
2015-10-28 23:32:30 +01:00
if ( $this -> _isRest ()) {
$sa = $this -> ShadowAttribute -> find (
'first' ,
array (
'conditions' => array ( 'ShadowAttribute.id' => $this -> ShadowAttribute -> id ),
'recursive' => - 1 ,
'fields' => array ( 'id' , 'old_id' , 'event_id' , 'type' , 'category' , 'value' , 'comment' , 'to_ids' , 'uuid' , 'event_org' , 'email' , 'deleted' , 'timestamp' )
)
);
$this -> set ( 'ShadowAttribute' , $sa [ 'ShadowAttribute' ]);
$this -> set ( '_serialize' , array ( 'ShadowAttribute' ));
} else {
$this -> Session -> setFlash ( __ ( 'The proposed Attribute has been saved' . $emailResult ));
$this -> redirect ( array ( 'controller' => 'events' , 'action' => 'view' , $existingAttribute [ 'Attribute' ][ 'event_id' ]));
}
2013-04-25 14:04:08 +02:00
} else {
2015-10-28 23:32:30 +01:00
if ( $this -> _isRest ()) {
$message = '' ;
foreach ( $this -> ShadowAttribute -> validationErrors as $k => $v ) {
$message .= '[' . $k . ']: ' . $v [ 0 ] . PHP_EOL ;
}
throw new NotFoundException ( 'Could not save the proposal. Errors: ' . $message );
} else {
$this -> Session -> setFlash ( __ ( 'The ShadowAttribute could not be saved. Please, try again.' ));
}
2013-04-25 14:04:08 +02:00
}
} else {
// Read the attribute that we're about to edit
$this -> ShadowAttribute -> create ();
2015-10-28 23:32:30 +01:00
$request [ 'ShadowAttribute' ] = $existingAttribute [ 'Attribute' ];
2013-04-25 14:04:08 +02:00
$this -> request -> data = $request ;
unset ( $this -> request -> data [ 'ShadowAttribute' ][ 'id' ]);
}
// combobox for types
$types = array_keys ( $this -> ShadowAttribute -> typeDefinitions );
$types = $this -> _arrayToValuesIndexArray ( $types );
$this -> set ( 'types' , $types );
// combobox for categories
$categories = $this -> ShadowAttribute -> validate [ 'category' ][ 'rule' ][ 1 ];
array_pop ( $categories ); // remove that last empty/space option
$categories = $this -> _arrayToValuesIndexArray ( $categories );
$this -> set ( 'categories' , $categories );
$this -> set ( 'attrDescriptions' , $this -> ShadowAttribute -> fieldDescriptions );
$this -> set ( 'typeDefinitions' , $this -> ShadowAttribute -> typeDefinitions );
$this -> set ( 'categoryDefinitions' , $this -> ShadowAttribute -> categoryDefinitions );
}
2013-09-03 15:29:44 +02:00
2015-10-28 23:32:30 +01:00
public function view ( $id ) {
$distConditions = array ();
if ( ! $this -> _isSiteAdmin ()) {
$distConditions = array (
'OR' => array (
'Event.distribution >' => 0 ,
'Event.org' => $this -> Auth -> user ( 'org' ),
'Event.orgc' => $this -> Auth -> user ( 'org' ),
),
);
}
$sa = $this -> ShadowAttribute -> find ( 'first' , array (
'recursive' => - 1 ,
'contain' => 'Event' ,
'fields' => array (
'ShadowAttribute.id' , 'ShadowAttribute.old_id' , 'ShadowAttribute.event_id' , 'ShadowAttribute.type' , 'ShadowAttribute.category' , 'ShadowAttribute.uuid' , 'ShadowAttribute.to_ids' , 'ShadowAttribute.value' , 'ShadowAttribute.comment' , 'ShadowAttribute.org' ,
'Event.id' , 'Event.orgc' , 'Event.org' , 'Event.distribution' , 'Event.uuid'
),
'conditions' => array ( 'AND' => array ( 'ShadowAttribute.id' => $id , $distConditions , 'ShadowAttribute.deleted' => 0 ))
));
if ( empty ( $sa )) throw new NotFoundException ( 'Invalid proposal.' );
if ( ! $this -> _isSiteAdmin ()) {
if ( $sa [ 'ShadowAttribute' ][ 'old_id' ] != 0 && $sa [ 'Event' ][ 'org' ] != $this -> Auth -> user ( 'org' ) && $sa [ 'Event' ][ 'orgc' ] != $this -> Auth -> user ( 'org' )) {
$a = $this -> ShadowAttribute -> Event -> Attribute -> find ( 'first' , array (
'recursive' => - 1 ,
'fields' => array ( 'Attribute.id' , 'Attribute.distribution' ),
'conditions' => array ( 'Attribute.id' => $sa [ 'ShadowAttribute' ][ 'old_id' ], 'Attribute.distribution >' => 0 )
));
if ( empty ( $a )) throw new NotFoundException ( 'Invalid proposal.' );
}
}
$this -> set ( 'ShadowAttribute' , $sa [ 'ShadowAttribute' ]);
$this -> set ( '_serialize' , array ( 'ShadowAttribute' ));
}
2013-09-03 15:29:44 +02:00
private function _setProposalLock ( $id , $lock = true ) {
2013-06-20 16:21:55 +02:00
$this -> loadModel ( 'Event' );
$this -> Event -> recursive = - 1 ;
$event = $this -> Event -> read ( null , $id );
2013-09-03 15:29:44 +02:00
if ( $lock ) {
$event [ 'Event' ][ 'proposal_email_lock' ] = 1 ;
2013-06-20 16:21:55 +02:00
} else {
2013-09-03 15:29:44 +02:00
$event [ 'Event' ][ 'proposal_email_lock' ] = 0 ;
2013-06-20 16:21:55 +02:00
}
2013-09-03 15:29:44 +02:00
$fieldList = array ( 'proposal_email_lock' , 'id' , 'info' );
$this -> Event -> save ( $event , array ( 'fieldList' => $fieldList ));
2013-06-20 16:21:55 +02:00
}
2013-09-03 15:29:44 +02:00
2013-06-20 16:21:55 +02:00
private function __sendProposalAlertEmail ( $id ) {
$this -> loadModel ( 'Event' );
$this -> Event -> recursive = - 1 ;
$event = $this -> Event -> read ( null , $id );
2013-09-03 15:29:44 +02:00
// If the event has an e-mail lock, return
if ( $event [ 'Event' ][ 'proposal_email_lock' ] == 1 ) {
return ;
} else {
$this -> _setProposalLock ( $id );
}
2014-08-18 13:30:18 +02:00
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 );
2013-06-20 16:21:55 +02:00
}
}
2014-08-18 13:30:18 +02:00
$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' ;
2015-06-09 13:04:33 +02:00
$gpg = new Crypt_GPG ( array ( 'homedir' => Configure :: read ( 'GnuPG.homedir' ), 'binary' => ( Configure :: read ( 'GnuPG.binary' ) ? Configure :: read ( 'GnuPG.binary' ) : '/usr/bin/gpg' )));
2014-08-18 13:30:18 +02:00
$gpg -> addSignKey ( Configure :: read ( 'GnuPG.email' ), Configure :: read ( 'GnuPG.password' ));
$bodySigned = $gpg -> sign ( $body , Crypt_GPG :: SIGN_MODE_CLEAR );
2013-06-20 16:21:55 +02:00
// Add the GPG key of the user as attachment
// LATER sign the attached GPG key
2014-08-18 13:30:18 +02:00
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
2013-06-20 16:21:55 +02:00
$this -> Email -> attachments = array (
2014-08-18 13:30:18 +02:00
'gpgkey.asc' => $tmpfname
2013-06-20 16:21:55 +02:00
);
}
2014-08-18 13:30:18 +02:00
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 {
2015-06-09 13:04:33 +02:00
$gpg = new Crypt_GPG ( array ( 'homedir' => Configure :: read ( 'GnuPG.homedir' ), 'binary' => ( Configure :: read ( 'GnuPG.binary' ) ? Configure :: read ( 'GnuPG.binary' ) : '/usr/bin/gpg' )));
2014-08-18 13:30:18 +02:00
$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' ];
2014-09-10 11:48:48 +02:00
$this -> Email -> subject = " [ " . Configure :: read ( 'MISP.org' ) . " MISP] Proposal to event # " . $id ;
2014-08-18 13:30:18 +02:00
$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 ;
2013-06-20 16:21:55 +02:00
}
2014-08-18 13:30:18 +02:00
return true ;
2013-06-20 16:21:55 +02:00
}
2013-09-03 15:29:44 +02:00
2015-10-31 00:18:05 +01:00
public function index ( $eventId = false ) {
$conditions = array ();
2013-09-11 10:35:50 +02:00
if ( ! $this -> _isSiteAdmin ()) {
$conditions = array ( 'Event.org =' => $this -> Auth -> user ( 'org' ));
}
2015-10-31 00:18:05 +01:00
if ( $eventId && is_numeric ( $eventId )) $conditions [ 'ShadowAttribute.event_id' ] = $eventId ;
2014-08-11 15:11:57 +02:00
$conditions [] = array ( 'deleted' => 0 );
2015-10-31 00:18:05 +01:00
if ( $this -> _isRest ()) {
$temp = $this -> ShadowAttribute -> find ( 'all' , array (
'conditions' => $conditions ,
'fields' => array ( 'ShadowAttribute.id' , 'ShadowAttribute.old_id' , 'ShadowAttribute.event_id' , 'ShadowAttribute.type' , 'ShadowAttribute.category' , 'ShadowAttribute.uuid' , 'ShadowAttribute.to_ids' , 'ShadowAttribute.value' , 'ShadowAttribute.comment' , 'ShadowAttribute.org' ),
'contain' => array (
'Event' => array (
'fields' => array ( 'id' , 'org' ),
),
),
));
if ( empty ( $temp )) throw new MethodNotAllowedException ( 'No proposals found or invalid event.' );
$proposals = array ();
foreach ( $temp as $proposal ) $proposals [] = $proposal [ 'ShadowAttribute' ];
$this -> set ( 'ShadowAttribute' , $proposals );
$this -> set ( '_serialize' , array ( 'ShadowAttribute' ));
} else {
$this -> paginate = array (
'conditions' => $conditions ,
'fields' => array ( 'id' , 'org' , 'old_id' , 'deleted' , 'value' , 'category' , 'type' ),
'contain' => array (
'Event' => array (
'fields' => array ( 'id' , 'org' , 'info' , 'orgc' ),
),
),
'recursive' => 1
);
$this -> set ( 'shadowAttributes' , $this -> paginate ());
}
2013-09-03 15:29:44 +02:00
}
2013-12-12 16:25:20 +01:00
2014-01-05 21:30:39 +01:00
private function _getEventData ( $event_id ) {
$this -> loadModel ( 'Event' );
$this -> Event -> recursive = - 1 ;
$this -> Event -> read ( array ( 'id' , 'uuid' , 'orgc' ), $event_id );
return $this -> Event -> data [ 'Event' ];
}
// takes a uuid and finds all proposals that belong to an event with the given uuid. These are then returned.
public function getProposalsByUuid ( $uuid ) {
2015-08-17 16:10:10 +02:00
if ( ! $this -> _isRest () || ! $this -> userRole [ 'perm_sync' ]) {
throw new MethodNotAllowedException ( __ ( 'This feature is only available using the API to Sync users' ));
2014-01-05 21:30:39 +01:00
}
if ( strlen ( $uuid ) != 36 ) {
throw new NotFoundException ( __ ( 'Invalid UUID' ));
}
$this -> ShadowAttribute -> recursive = - 1 ;
$temp = $this -> ShadowAttribute -> findAllByEventUuid ( $uuid );
if ( $temp == null ) throw new NotFoundException ( __ ( 'Invalid event' ));
$this -> set ( 'proposal' , $temp );
$this -> render ( 'get_proposals_by_uuid' );
}
2014-05-02 14:29:15 +02:00
public function fetchEditForm ( $id , $field = null ) {
$validFields = array ( 'value' , 'comment' , 'type' , 'category' , 'to_ids' );
if ( ! isset ( $field ) || ! in_array ( $field , $validFields )) throw new MethodNotAllowedException ( 'Invalid field requested.' );
//if (!$this->request->is('ajax')) throw new MethodNotAllowedException('This function can only be accessed via AJAX.');
$this -> loadModel ( 'Attribute' );
$this -> Attribute -> id = $id ;
if ( ! $this -> Attribute -> exists ()) {
throw new NotFoundException ( __ ( 'Invalid attribute' ));
}
$fields = array ( 'id' , 'distribution' , 'event_id' );
$additionalFieldsToLoad = $field ;
if ( $field == 'category' || $field == 'type' ) {
$fields [] = 'type' ;
$fields [] = 'category' ;
} else {
$fields [] = $field ;
}
$attribute = $this -> Attribute -> find ( 'first' , array (
'recursive' => - 1 ,
'conditions' => array ( 'Attribute.id' => $id ),
'fields' => $fields ,
'contain' => array (
'Event' => array (
'fields' => array ( 'distribution' , 'id' , 'user_id' , 'orgc' , 'org' ),
)
)
));
if ( ! $this -> _isSiteAdmin ()) {
//
if ( $attribute [ 'Event' ][ 'orgc' ] != $this -> Auth -> user ( 'org' ) && ( $attribute [ 'Event' ][ 'org' ] == $this -> Auth -> user ( 'org' ) || $attribute [ 'Event' ][ 'distribution' ] > 0 )) {
// Allow the edit
} else {
throw new NotFoundException ( __ ( 'Invalid attribute' ));
}
}
$this -> layout = 'ajax' ;
if ( $field == 'distribution' ) $this -> set ( 'distributionLevels' , $this -> Attribute -> distributionLevels );
if ( $field == 'category' ) {
$typeCategory = array ();
foreach ( $this -> Attribute -> categoryDefinitions as $k => $category ) {
foreach ( $category [ 'types' ] as $type ) {
$typeCategory [ $type ][] = $k ;
}
}
$this -> set ( 'typeCategory' , $typeCategory );
}
if ( $field == 'type' ) {
$this -> set ( 'categoryDefinitions' , $this -> Attribute -> categoryDefinitions );
}
$this -> set ( 'object' , $attribute [ 'Attribute' ]);
$fieldURL = ucfirst ( $field );
$this -> render ( 'ajax/attributeEdit' . $fieldURL . 'Form' );
}
// ajax edit - post a single edited field and this method will attempt to create a proposal and return a json with the validation errors if they occur.
public function editField ( $id ) {
if (( ! $this -> request -> is ( 'post' ) && ! $this -> request -> is ( 'put' )) || ! $this -> request -> is ( 'ajax' )) throw new MethodNotAllowedException ();
$this -> LoadModel ( 'Attribute' );
$this -> Attribute -> id = $id ;
if ( ! $this -> Attribute -> exists ()) {
throw new NotFoundException ( __ ( 'Invalid attribute' ));
}
$this -> Attribute -> recursive = - 1 ;
$this -> Attribute -> contain ( 'Event' );
$attribute = $this -> Attribute -> read ();
if ( ! $this -> _isSiteAdmin ()) {
//
if ( $attribute [ 'Event' ][ 'orgc' ] != $this -> Auth -> user ( 'org' ) && ( $attribute [ 'Event' ][ 'org' ] == $this -> Auth -> user ( 'org' ) || $attribute [ 'Event' ][ 'distribution' ] > 0 )) {
// Allow the edit
} else {
throw new NotFoundException ( __ ( 'Invalid attribute' ));
}
}
$keys = array_flip ( array ( 'uuid' , 'event_id' , 'value' , 'type' , 'category' , 'to_ids' ));
$proposal = array_intersect_key ( $attribute [ 'Attribute' ], $keys );
$proposal [ 'email' ] = $this -> Auth -> user ( 'email' );
$proposal [ 'org' ] = $this -> Auth -> user ( 'org' );
$proposal [ 'event_uuid' ] = $attribute [ 'Event' ][ 'uuid' ];
$proposal [ 'event_org' ] = $attribute [ 'Event' ][ 'orgc' ];
2014-05-02 15:19:15 +02:00
$proposal [ 'old_id' ] = $attribute [ 'Attribute' ][ 'id' ];
2014-05-02 14:29:15 +02:00
foreach ( $this -> request -> data [ 'ShadowAttribute' ] as $changedKey => $changedField ) {
2014-05-05 10:13:40 +02:00
if ( $proposal [ $changedKey ] == $changedField ) {
2014-05-02 14:29:15 +02:00
$this -> autoRender = false ;
return new CakeResponse ( array ( 'body' => json_encode ( 'nochange' ), 'status' => 200 ));
}
$proposal [ $changedKey ] = $changedField ;
}
if ( $this -> ShadowAttribute -> save ( $proposal )) {
$this -> autoRender = false ;
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => true )), 'status' => 200 ));
} else {
$this -> autoRender = false ;
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'fail' => false , 'errors' => $this -> ShadowAttribute -> validationErrors )), 'status' => 200 ));
}
}
2015-06-25 11:51:36 +02:00
public function discardSelected ( $id ) {
2015-11-13 23:57:03 +01:00
if ( ! $this -> request -> is ( 'post' ) || ! $this -> request -> is ( 'ajax' )) throw new MethodNotAllowedException ();
2015-06-25 11:51:36 +02:00
// get a json object with a list of proposal IDs to be discarded
// check each of them and return a json object with the successful discards and the failed ones.
$ids = json_decode ( $this -> request -> data [ 'ShadowAttribute' ][ 'ids_discard' ]);
if ( ! $this -> _isSiteAdmin ()) {
$event = $this -> ShadowAttribute -> Event -> find ( 'first' , array (
'conditions' => array ( 'id' => $id ),
'recursive' => - 1 ,
'fields' => array ( 'id' , 'orgc' , 'user_id' )
));
if ( $event [ 'Event' ][ 'orgc' ] != $this -> Auth -> user ( 'org' ) || ( ! $this -> userRole [ 'perm_modify_org' ] && ! ( $this -> userRole [ 'perm_modify' ] && $event [ 'Event' ][ 'user_id' ] == $this -> Auth -> user ( 'id' )))) {
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'false' => true , 'errors' => 'You don\'t have permission to do that.' )), 'status' => 200 ));
}
}
// find all attributes from the ID list that also match the provided event ID.
$shadowAttributes = $this -> ShadowAttribute -> find ( 'all' , array (
'recursive' => - 1 ,
'conditions' => array ( 'id' => $ids , 'event_id' => $id ),
'fields' => array ( 'id' , 'event_id' )
));
$successes = array ();
foreach ( $shadowAttributes as $a ) {
if ( $this -> discard ( $a [ 'ShadowAttribute' ][ 'id' ])) $successes [] = $a [ 'ShadowAttribute' ][ 'id' ];
}
$fails = array_diff ( $ids , $successes );
$this -> autoRender = false ;
if ( count ( $fails ) == 0 && count ( $successes ) > 0 ) {
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => true , 'success' => count ( $successes ) . ' proposal' . ( count ( $successes ) != 1 ? 's' : '' ) . ' deleted.' )), 'status' => 200 ));
} else {
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => false , 'errors' => count ( $successes ) . ' proposal' . ( count ( $successes ) != 1 ? 's' : '' ) . ' deleted, but ' . count ( $fails ) . ' proposal' . ( count ( $fails ) != 1 ? 's' : '' ) . ' could not be deleted.' )), 'status' => 200 ));
}
}
public function acceptSelected ( $id ) {
2015-11-13 23:57:03 +01:00
if ( ! $this -> request -> is ( 'post' ) || ! $this -> request -> is ( 'ajax' )) throw new MethodNotAllowedException ();
2015-06-25 11:51:36 +02:00
// get a json object with a list of proposal IDs to be accepted
// check each of them and return a json object with the successful accepts and the failed ones.
$ids = json_decode ( $this -> request -> data [ 'ShadowAttribute' ][ 'ids_accept' ]);
if ( ! $this -> _isSiteAdmin ()) {
$event = $this -> ShadowAttribute -> Event -> find ( 'first' , array (
'conditions' => array ( 'id' => $id ),
'recursive' => - 1 ,
'fields' => array ( 'id' , 'orgc' , 'user_id' )
));
if ( $event [ 'Event' ][ 'orgc' ] != $this -> Auth -> user ( 'org' ) || ( ! $this -> userRole [ 'perm_modify_org' ] && ! ( $this -> userRole [ 'perm_modify' ] && $event [ 'Event' ][ 'user_id' ] == $this -> Auth -> user ( 'id' )))) {
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'false' => true , 'errors' => 'You don\'t have permission to do that.' )), 'status' => 200 ));
}
}
// find all attributes from the ID list that also match the provided event ID.
$shadowAttributes = $this -> ShadowAttribute -> find ( 'all' , array (
'recursive' => - 1 ,
'conditions' => array ( 'id' => $ids , 'event_id' => $id ),
'fields' => array ( 'id' , 'event_id' )
));
$successes = array ();
foreach ( $shadowAttributes as $a ) {
$response = $this -> __accept ( $a [ 'ShadowAttribute' ][ 'id' ]);
if ( isset ( $response [ 'saved' ])) $successes [] = $a [ 'ShadowAttribute' ][ 'id' ];
}
$fails = array_diff ( $ids , $successes );
$this -> autoRender = false ;
if ( count ( $fails ) == 0 && count ( $successes ) > 0 ) {
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => true , 'success' => count ( $successes ) . ' proposal' . ( count ( $successes ) != 1 ? 's' : '' ) . ' deleted.' )), 'status' => 200 ));
} else {
return new CakeResponse ( array ( 'body' => json_encode ( array ( 'saved' => false , 'errors' => count ( $successes ) . ' proposal' . ( count ( $successes ) != 1 ? 's' : '' ) . ' deleted, but ' . count ( $fails ) . ' proposal' . ( count ( $fails ) != 1 ? 's' : '' ) . ' could not be deleted.' )), 'status' => 200 ));
}
}
2013-04-25 14:04:08 +02:00
}