2024-01-04 10:12:47 +01:00
< ? php
App :: uses ( 'AppModel' , 'Model' );
2024-02-01 14:24:41 +01:00
App :: uses ( 'ServerSyncTool' , 'Tools' );
2024-01-04 10:12:47 +01:00
class AnalystData extends AppModel
{
public $recursive = - 1 ;
public $actsAs = array (
2024-02-13 14:37:00 +01:00
'AuditLog' ,
'Containable'
2024-01-04 10:12:47 +01:00
);
public $valid_targets = [
'Attribute' ,
'Event' ,
2024-01-30 15:15:26 +01:00
'EventReport' ,
2024-01-04 10:12:47 +01:00
'GalaxyCluster' ,
'Galaxy' ,
'Object' ,
'Note' ,
'Opinion' ,
'Relationship' ,
'Organisation' ,
'SharingGroup'
];
2024-01-24 21:48:53 +01:00
const NOTE = 0 ,
OPINION = 1 ,
RELATIONSHIP = 2 ;
2024-02-01 14:24:41 +01:00
const ANALYST_DATA_TYPES = [
'Note' ,
'Opinion' ,
'Relationship' ,
];
2024-02-22 08:32:47 +01:00
protected const BASE_EDITABLE_FIELDS = [
'language' ,
'authors' ,
'modified' ,
'distribution' ,
'sharing_group_id' ,
];
2024-04-11 10:03:32 +02:00
public const EDITABLE_FIELDS = [];
2024-02-22 08:32:47 +01:00
2024-01-29 12:00:13 +01:00
/** @var object|null */
protected $Note ;
/** @var object|null */
protected $Opinion ;
/** @var object|null */
2024-02-01 14:24:41 +01:00
protected $Relationship ;
/** @var object|null */
2024-01-29 12:00:13 +01:00
protected $ObjectRelationship ;
2024-01-30 08:57:14 +01:00
/** @var object|null */
protected $User ;
/** @var object|null */
2024-02-01 14:24:41 +01:00
public $Org ;
/** @var object|null */
public $Orgc ;
2024-01-30 08:57:14 +01:00
/** @var object|null */
public $SharingGroup ;
2024-02-13 11:13:39 +01:00
/** @var array */
protected $fetchedUUIDFromRecursion = [];
2024-01-29 12:00:13 +01:00
2024-01-04 10:12:47 +01:00
public $current_user = null ;
2024-01-30 08:57:14 +01:00
public $belongsTo = [
'SharingGroup' => [
'className' => 'SharingGroup' ,
'foreignKey' => 'sharing_group_id'
2024-02-01 14:24:41 +01:00
],
2024-01-30 08:57:14 +01:00
];
2024-01-24 21:48:53 +01:00
public function __construct ( $id = false , $table = null , $ds = null )
{
parent :: __construct ( $id , $table , $ds );
$this -> bindModel ([
'belongsTo' => [
2024-02-01 14:24:41 +01:00
'Org' => [
2024-01-24 21:48:53 +01:00
'className' => 'Organisation' ,
2024-02-27 09:03:17 +01:00
'fields' => [
'id' , 'name' , 'uuid' , 'type' , 'description' , 'sector' , 'nationality' , 'local'
],
2024-01-24 21:48:53 +01:00
'foreignKey' => false ,
'conditions' => [
2024-02-01 14:24:41 +01:00
sprintf ( '%s.org_uuid = Org.uuid' , $this -> alias )
],
],
'Orgc' => [
'className' => 'Organisation' ,
2024-02-27 09:03:17 +01:00
'fields' => [
'id' , 'name' , 'uuid' , 'type' , 'sector' , 'nationality' , 'local'
],
2024-02-01 14:24:41 +01:00
'foreignKey' => false ,
'conditions' => [
sprintf ( '%s.orgc_uuid = Orgc.uuid' , $this -> alias )
2024-01-24 21:48:53 +01:00
],
2024-01-30 08:57:14 +01:00
],
'SharingGroup' => [
'className' => 'SharingGroup' ,
2024-02-27 09:03:17 +01:00
'fields' => [
2024-02-27 09:06:15 +01:00
'id' , 'name' , 'uuid' , 'releasability' , 'description' , 'org_id' , 'active' , 'roaming' , 'local'
2024-02-27 09:03:17 +01:00
],
2024-01-30 08:57:14 +01:00
'foreignKey' => false ,
'conditions' => [
sprintf ( '%s.sharing_group_id = SharingGroup.id' , $this -> alias )
],
],
2024-01-24 21:48:53 +01:00
]
]);
2024-02-01 14:24:41 +01:00
$this -> Org = ClassRegistry :: init ( 'Organisation' );
$this -> Orgc = ClassRegistry :: init ( 'Organisation' );
2024-01-24 21:48:53 +01:00
}
public function afterFind ( $results , $primary = false )
{
parent :: afterFind ( $results , $primary );
2024-01-30 08:57:14 +01:00
$this -> setUser ();
2024-01-24 21:48:53 +01:00
foreach ( $results as $i => $v ) {
$results [ $i ][ $this -> alias ][ 'note_type' ] = $this -> current_type_id ;
$results [ $i ][ $this -> alias ][ 'note_type_name' ] = $this -> current_type ;
2024-01-30 08:57:14 +01:00
$results [ $i ] = $this -> rearrangeOrganisation ( $results [ $i ], $this -> current_user );
$results [ $i ] = $this -> rearrangeSharingGroup ( $results [ $i ], $this -> current_user );
2024-01-30 15:15:26 +01:00
$results [ $i ][ $this -> alias ][ '_canEdit' ] = $this -> canEditAnalystData ( $this -> current_user , $v , $this -> alias );
2024-02-01 14:24:41 +01:00
if ( ! empty ( $this -> fetchRecursive ) && ! empty ( $results [ $i ][ $this -> alias ][ 'uuid' ])) {
2024-02-13 14:37:00 +01:00
$this -> Note = ClassRegistry :: init ( 'Note' );
$this -> Opinion = ClassRegistry :: init ( 'Opinion' );
$this -> Note -> fetchRecursive = false ;
$this -> Opinion -> fetchRecursive = false ;
2024-02-01 14:24:41 +01:00
$results [ $i ][ $this -> alias ] = $this -> fetchChildNotesAndOpinions ( $this -> current_user , $results [ $i ][ $this -> alias ]);
2024-02-13 14:37:00 +01:00
$this -> Note -> fetchRecursive = true ;
$this -> Opinion -> fetchRecursive = true ;
2024-01-24 21:48:53 +01:00
}
}
return $results ;
}
2024-01-04 10:12:47 +01:00
public function beforeValidate ( $options = array ())
{
2024-02-21 16:17:44 +01:00
parent :: beforeValidate ( $options );
2024-01-04 10:12:47 +01:00
if ( empty ( $this -> id ) && empty ( $this -> data [ $this -> current_type ][ 'uuid' ])) {
$this -> data [ $this -> current_type ][ 'uuid' ] = CakeText :: uuid ();
}
if ( empty ( $this -> id )) {
if ( empty ( $this -> data [ $this -> current_type ][ 'orgc_uuid' ]) || empty ( $this -> current_user [ 'Role' ][ 'perm_sync' ])) {
$this -> data [ $this -> current_type ][ 'orgc_uuid' ] = $this -> current_user [ 'Organisation' ][ 'uuid' ];
}
$this -> data [ $this -> current_type ][ 'org_uuid' ] = $this -> current_user [ 'Organisation' ][ 'uuid' ];
2024-02-07 11:29:35 +01:00
if ( empty ( $this -> data [ $this -> current_type ][ 'authors' ])) {
$this -> data [ $this -> current_type ][ 'authors' ] = $this -> current_user [ 'email' ];
}
2024-01-04 10:12:47 +01:00
}
2024-02-27 08:24:24 +01:00
if ( isset ( $this -> data [ $this -> current_type ][ 'distribution' ])) {
if (
$this -> data [ $this -> current_type ][ 'distribution' ] != 4 &&
(
isset ( $this -> data [ $this -> current_type ][ 'sharing_group_id' ]) &&
$this -> data [ $this -> current_type ][ 'sharing_group_id' ] != 0
)
) {
$this -> data [ $this -> current_type ][ 'sharing_group_id' ] = 0 ;
}
}
2024-01-04 10:12:47 +01:00
return true ;
}
2024-02-21 16:17:44 +01:00
public function beforeSave ( $options = [])
{
parent :: beforeSave ( $options );
if ( empty ( $this -> data [ $this -> current_type ][ 'created' ])) {
2024-02-23 10:18:29 +01:00
$this -> data [ $this -> current_type ][ 'created' ] = ( new DateTime ()) -> format ( 'Y-m-d H:i:s' );
2024-02-21 16:17:44 +01:00
}
if ( empty ( $this -> data [ $this -> current_type ][ 'modified' ])) {
2024-02-23 10:18:29 +01:00
$this -> data [ $this -> current_type ][ 'modified' ] = ( new DateTime ()) -> format ( 'Y-m-d H:i:s' );
2024-02-21 16:17:44 +01:00
}
2024-02-23 10:18:29 +01:00
$this -> data [ $this -> current_type ][ 'modified' ] = ( new DateTime ( $this -> data [ $this -> current_type ][ 'modified' ], new DateTimeZone ( 'UTC' ))) -> format ( 'Y-m-d H:i:s' );
$this -> data [ $this -> current_type ][ 'created' ] = ( new DateTime ( $this -> data [ $this -> current_type ][ 'created' ], new DateTimeZone ( 'UTC' ))) -> format ( 'Y-m-d H:i:s' );
2024-02-21 16:17:44 +01:00
return true ;
}
2024-02-22 08:32:47 +01:00
public function getEditableFields () : array
{
2024-04-11 10:03:32 +02:00
return array_merge ( static :: BASE_EDITABLE_FIELDS , static :: EDITABLE_FIELDS );
2024-02-22 08:32:47 +01:00
}
2024-01-30 15:15:26 +01:00
/**
* Checks if user can modify given analyst data
*
* @ param array $user
* @ param array $analystData
* @ return bool
*/
public function canEditAnalystData ( array $user , array $analystData , $modelType ) : bool
{
if ( ! isset ( $analystData [ $modelType ])) {
2024-02-13 16:02:12 +01:00
return false ; // This can happen when using find('count')
2024-01-30 15:15:26 +01:00
}
if ( $user [ 'Role' ][ 'perm_site_admin' ]) {
return true ;
}
2024-02-01 14:24:41 +01:00
if ( isset ( $analystData [ $modelType ][ 'orgc_uuid' ]) && $analystData [ $modelType ][ 'orgc_uuid' ] == $user [ 'Organisation' ][ 'uuid' ]) {
2024-01-30 15:15:26 +01:00
return true ;
}
return false ;
}
public function buildConditions ( array $user ) : array
{
$conditions = [];
if ( ! $user [ 'Role' ][ 'perm_site_admin' ]) {
$sgids = $this -> SharingGroup -> authorizedIds ( $user );
$alias = $this -> alias ;
2024-02-06 11:15:05 +01:00
$prefix = $alias != 'AnalystData' ? " { $alias } . " : '' ;
2024-01-30 15:15:26 +01:00
$conditions [ 'AND' ][ 'OR' ] = [
2024-02-06 11:15:05 +01:00
" { $prefix } org_uuid " => $user [ 'Organisation' ][ 'uuid' ],
2024-01-30 15:15:26 +01:00
[
'AND' => [
2024-02-06 11:15:05 +01:00
" { $prefix } distribution > " => 0 ,
" { $prefix } distribution < " => 4
2024-01-30 15:15:26 +01:00
],
],
[
'AND' => [
2024-02-06 11:15:05 +01:00
" { $prefix } sharing_group_id " => $sgids ,
" { $prefix } distribution " => 4
2024-01-30 15:15:26 +01:00
]
]
];
}
return $conditions ;
}
2024-01-30 08:57:14 +01:00
protected function setUser ()
{
if ( empty ( $this -> current_user )) {
$user_id = Configure :: read ( 'CurrentUserId' );
$this -> User = ClassRegistry :: init ( 'User' );
if ( $user_id ) {
$this -> current_user = $this -> User -> getAuthUser ( $user_id );
}
}
}
private function rearrangeOrganisation ( array $analystData ) : array
{
if ( ! empty ( $analystData [ $this -> alias ][ 'orgc_uuid' ])) {
2024-02-01 14:24:41 +01:00
if ( ! isset ( $analystData [ 'Orgc' ])) {
$analystData [ $this -> alias ][ 'Orgc' ] = $this -> Orgc -> find ( 'first' , [ 'conditions' => [ 'uuid' => $analystData [ $this -> alias ][ 'orgc_uuid' ]]])[ 'Organisation' ];
} else {
$analystData [ $this -> alias ][ 'Orgc' ] = $analystData [ 'Orgc' ];
}
unset ( $analystData [ 'Orgc' ]);
}
if ( ! empty ( $analystData [ $this -> alias ][ 'org_uuid' ])) {
if ( ! isset ( $analystData [ 'Org' ])) {
$analystData [ $this -> alias ][ 'Org' ] = $this -> Org -> find ( 'first' , [ 'conditions' => [ 'uuid' => $analystData [ $this -> alias ][ 'org_uuid' ]]])[ 'Organisation' ];
2024-01-30 08:57:14 +01:00
} else {
2024-02-01 14:24:41 +01:00
$analystData [ $this -> alias ][ 'Org' ] = $analystData [ 'Org' ];
2024-01-30 08:57:14 +01:00
}
2024-02-01 14:24:41 +01:00
unset ( $analystData [ 'Org' ]);
2024-01-30 08:57:14 +01:00
}
return $analystData ;
}
private function rearrangeSharingGroup ( array $analystData , array $user ) : array
{
2024-01-30 09:36:45 +01:00
if ( isset ( $analystData [ $this -> alias ][ 'distribution' ])) {
if ( $analystData [ $this -> alias ][ 'distribution' ] == 4 ) {
if ( ! isset ( $analystData [ 'SharingGroup' ])) {
$this -> SharingGroup = ClassRegistry :: init ( 'SharingGroup' );
$sg = $this -> SharingGroup -> fetchSG ( $analystData [ $this -> alias ][ 'sharing_group_id' ], $user , true );
2024-02-27 09:03:17 +01:00
$sgData = array_intersect_key (
$sg [ 'SharingGroup' ], array_flip (
[
'id' , 'name' , 'uuid' , 'releasability' , 'description' , 'org_id' ,
2024-02-27 09:06:15 +01:00
'active' , 'roaming' , 'local'
2024-02-27 09:03:17 +01:00
]
)
);
$analystData [ $this -> alias ][ 'SharingGroup' ] = $sgData ;
2024-01-30 09:36:45 +01:00
} else {
$analystData [ $this -> alias ][ 'SharingGroup' ] = $analystData [ 'SharingGroup' ];
}
2024-01-30 08:57:14 +01:00
} else {
2024-01-30 09:36:45 +01:00
unset ( $analystData [ 'SharingGroup' ]);
2024-01-30 08:57:14 +01:00
}
}
return $analystData ;
}
2024-01-04 10:12:47 +01:00
public function deduceType ( string $uuid )
{
foreach ( $this -> valid_targets as $valid_target ) {
$this -> { $valid_target } = ClassRegistry :: init ( $valid_target );
$result = $this -> $valid_target -> find ( 'first' , [
'conditions' => [ $valid_target . '.uuid' => $uuid ],
'recursive' => - 1
]);
if ( ! empty ( $result )) {
return $valid_target ;
}
}
throw new NotFoundException ( __ ( 'Invalid UUID' ));
}
2024-01-24 21:48:53 +01:00
2024-02-16 10:30:26 +01:00
public function getAnalystDataTypeFromUUID ( $uuid )
{
foreach ( self :: ANALYST_DATA_TYPES as $type ) {
$this -> { $type } = ClassRegistry :: init ( $type );
$result = $this -> { $type } -> find ( 'first' , [
'conditions' => [ $type . '.uuid' => $uuid ],
'recursive' => - 1
]);
if ( ! empty ( $result )) {
return $type ;
}
}
throw new NotFoundException ( __ ( 'Invalid UUID' ));
}
2024-02-01 14:24:41 +01:00
public function deduceAnalystDataType ( array $analystData )
{
2024-02-14 09:48:11 +01:00
if ( ! empty ( $analystData [ 'note_type_name' ]) && in_array ( $analystData [ 'note_type_name' ], self :: ANALYST_DATA_TYPES )) {
return $analystData [ 'note_type_name' ];
}
2024-02-01 14:24:41 +01:00
foreach ( self :: ANALYST_DATA_TYPES as $type ) {
if ( isset ( $analystData [ $type ])) {
return $type ;
}
}
throw new NotFoundException ( __ ( 'Invalid or could not deduce analyst data type' ));
}
2024-02-15 13:30:16 +01:00
public function getIDFromUUID ( $type , $id ) : int
{
2024-02-16 10:30:26 +01:00
$tmpForID = $this -> find ( 'first' , [
2024-02-15 13:30:16 +01:00
'conditions' => [
'uuid' => $id ,
],
'fields' => [ 'id' , 'uuid' ,],
]);
$id = - 1 ;
if ( ! empty ( $tmpForID )) {
$id = $tmpForID [ $type ][ 'id' ];
}
return $id ;
}
2024-02-13 15:38:23 +01:00
public function fetchSimple ( array $user , $id ) : array
{
$conditions = [
'AND' => [
$this -> buildConditions ( $user )
],
];
if ( Validation :: uuid ( $id )) {
$conditions [ $this -> alias . '.uuid' ] = $id ;
} else {
$conditions [ $this -> alias . '.id' ] = $id ;
}
return $this -> find ( 'first' , [
'conditions' => $conditions ,
'contain' => [ 'Org' , 'Orgc' ],
]);
}
2024-02-13 10:23:09 +01:00
public function fetchChildNotesAndOpinions ( array $user , array $analystData , $depth = 2 ) : array
2024-01-24 21:48:53 +01:00
{
2024-02-13 11:13:39 +01:00
if ( $depth == 0 || ! empty ( $this -> fetchedUUIDFromRecursion [ $analystData [ 'uuid' ]])) {
2024-02-13 14:37:00 +01:00
$hasMoreNotesOrOpinions = $this -> hasMoreNotesOrOpinions ( $analystData , $user );
$analystData [ '_max_depth_reached' ] = $hasMoreNotesOrOpinions ;
2024-02-13 10:23:09 +01:00
return $analystData ;
}
2024-02-13 11:13:39 +01:00
$this -> fetchedUUIDFromRecursion [ $analystData [ 'uuid' ]] = true ;
2024-04-03 16:42:18 +02:00
$this -> Note = ClassRegistry :: init ( 'Note' );
$this -> Opinion = ClassRegistry :: init ( 'Opinion' );
2024-02-13 10:23:09 +01:00
2024-01-24 21:48:53 +01:00
$paramsNote = [
'recursive' => - 1 ,
2024-02-01 14:24:41 +01:00
'contain' => [ 'Org' , 'Orgc' ],
2024-01-24 21:48:53 +01:00
'conditions' => [
2024-02-01 14:24:41 +01:00
'AND' => [
2024-02-06 10:04:20 +01:00
$this -> Note -> buildConditions ( $user )
2024-02-01 14:24:41 +01:00
],
2024-02-06 10:04:20 +01:00
'object_type' => $analystData [ 'note_type_name' ],
2024-01-24 21:48:53 +01:00
'object_uuid' => $analystData [ 'uuid' ],
]
];
$paramsOpinion = [
'recursive' => - 1 ,
2024-02-01 14:24:41 +01:00
'contain' => [ 'Org' , 'Orgc' ],
2024-01-24 21:48:53 +01:00
'conditions' => [
2024-02-01 14:24:41 +01:00
'AND' => [
2024-02-06 10:04:20 +01:00
$this -> Opinion -> buildConditions ( $user )
2024-02-01 14:24:41 +01:00
],
2024-02-06 10:04:20 +01:00
'object_type' => $analystData [ 'note_type_name' ],
2024-01-24 21:48:53 +01:00
'object_uuid' => $analystData [ 'uuid' ],
]
];
2024-01-26 16:25:09 +01:00
2024-01-24 21:48:53 +01:00
// recursively fetch and include nested notes and opinions
2024-02-13 10:23:09 +01:00
$childNotes = array_map ( function ( $item ) use ( $user , $depth ) {
$expandedNotes = $this -> fetchChildNotesAndOpinions ( $user , $item [ 'Note' ], $depth - 1 );
2024-01-24 21:48:53 +01:00
return $expandedNotes ;
}, $this -> Note -> find ( 'all' , $paramsNote ));
2024-02-13 10:23:09 +01:00
$childOpinions = array_map ( function ( $item ) use ( $user , $depth ) {
$expandedNotes = $this -> fetchChildNotesAndOpinions ( $user , $item [ 'Opinion' ], $depth - 1 );
2024-01-24 21:48:53 +01:00
return $expandedNotes ;
}, $this -> Opinion -> find ( 'all' , $paramsOpinion ));
if ( ! empty ( $childNotes )) {
2024-02-13 11:13:39 +01:00
foreach ( $childNotes as $childNote ) {
$this -> fetchedUUIDFromRecursion [ $childNote [ 'uuid' ]] = true ;
}
2024-02-06 10:04:20 +01:00
$analystData [ 'Note' ] = $childNotes ;
2024-01-24 21:48:53 +01:00
}
if ( ! empty ( $childOpinions )) {
2024-02-13 11:13:39 +01:00
foreach ( $childNotes as $childNote ) {
$this -> fetchedUUIDFromRecursion [ $childNote [ 'uuid' ]] = true ;
}
2024-02-06 10:04:20 +01:00
$analystData [ 'Opinion' ] = $childOpinions ;
2024-01-24 21:48:53 +01:00
}
return $analystData ;
}
2024-01-29 11:01:02 +01:00
2024-02-13 14:37:00 +01:00
protected function hasMoreNotesOrOpinions ( $analystData , array $user ) : bool
{
$hasMoreNotes = $this -> Note -> find ( 'first' , [
'recursive' => - 1 ,
'conditions' => [
'AND' => [
$this -> Note -> buildConditions ( $user )
],
'object_type' => $analystData [ 'note_type_name' ],
'object_uuid' => $analystData [ 'uuid' ],
]
]);
if ( ! empty ( $hasMoreNotes )) {
return true ;
}
2024-02-29 14:24:45 +01:00
$hasMoreOpinions = $this -> Opinion -> find ( 'first' , [
2024-02-13 14:37:00 +01:00
'recursive' => - 1 ,
'conditions' => [
'AND' => [
$this -> Opinion -> buildConditions ( $user )
],
'object_type' => $analystData [ 'note_type_name' ],
'object_uuid' => $analystData [ 'uuid' ],
]
]);
if ( ! empty ( $hasMoreOpinions )) {
return true ;
}
return false ;
}
2024-01-29 11:01:02 +01:00
public function getExistingRelationships ()
{
$existingRelationships = $this -> find ( 'column' , [
'recursive' => - 1 ,
'fields' => [ 'relationship_type' ],
'unique' => true ,
]);
$this -> ObjectRelationship = ClassRegistry :: init ( 'ObjectRelationship' );
$objectRelationships = $this -> ObjectRelationship -> find ( 'column' , [
'recursive' => - 1 ,
'fields' => [ 'name' ],
'unique' => true ,
]);
return array_unique ( array_merge ( $existingRelationships , $objectRelationships ));
}
2024-01-31 15:10:08 +01:00
2024-02-13 15:38:23 +01:00
public function getChildren ( $user , $uuid , $depth = 2 ) : array
{
$analystData = $this -> fetchSimple ( $user , $uuid );
if ( empty ( $analystData )) {
return [];
}
$analystData = $analystData [ $this -> alias ];
$this -> Note = ClassRegistry :: init ( 'Note' );
$this -> Opinion = ClassRegistry :: init ( 'Opinion' );
$analystData = $this -> fetchChildNotesAndOpinions ( $user , $analystData , $depth );
return $analystData ;
}
2024-01-31 15:10:08 +01:00
/**
2024-02-01 14:24:41 +01:00
* Gets a cluster then save it .
*
* @ param array $user
* @ param array $analystData Analyst data to be saved
* @ param bool $fromPull If the current capture is performed from a PULL sync
* @ param int $orgId The organisation id that should own the analyst data
* @ param array $server The server for which to capture is ongoing
* @ return array Result of the capture including successes , fails and errors
*/
public function captureAnalystData ( array $user , array $analystData , $fromPull = false , $orgUUId = false , $server = false ) : array
{
2024-02-14 11:22:43 +01:00
$this -> Note = ClassRegistry :: init ( 'Note' );
$this -> Opinion = ClassRegistry :: init ( 'Opinion' );
$this -> Relationship = ClassRegistry :: init ( 'Relationship' );
2024-02-01 14:24:41 +01:00
$results = [ 'success' => false , 'imported' => 0 , 'ignored' => 0 , 'failed' => 0 , 'errors' => []];
$type = $this -> deduceAnalystDataType ( $analystData );
2024-02-14 09:48:11 +01:00
if ( ! isset ( $analystData [ $type ])) {
$analystData = [ $type => $analystData ];
}
2024-02-01 14:24:41 +01:00
$analystModel = ClassRegistry :: init ( $type );
if ( $fromPull && ! empty ( $orgUUId )) {
$analystData [ $type ][ 'org_uuid' ] = $orgUUId ;
} else {
$analystData [ $type ][ 'org_uuid' ] = $user [ 'Organisation' ][ 'uuid' ];
}
$this -> AnalystDataBlocklist = ClassRegistry :: init ( 'AnalystDataBlocklist' );
if ( $this -> AnalystDataBlocklist -> checkIfBlocked ( $analystData [ $type ][ 'uuid' ])) {
$results [ 'errors' ][] = __ ( 'Blocked by blocklist' );
$results [ 'ignored' ] ++ ;
return $results ;
}
2024-02-05 10:59:20 +01:00
if ( ! isset ( $analystData [ $type ][ 'orgc_uuid' ]) && ! isset ( $analystData [ $type ][ 'Orgc' ])) {
2024-02-01 14:24:41 +01:00
$analystData [ $type ][ 'orgc_uuid' ] = $analystData [ $type ][ 'org_uuid' ];
} else {
if ( ! isset ( $analystData [ $type ][ 'Orgc' ])) {
if ( isset ( $analystData [ $type ][ 'orgc_uuid' ]) && $analystData [ $type ][ 'orgc_uuid' ] != $user [ 'Organisation' ][ 'uuid' ] && ! $user [ 'Role' ][ 'perm_sync' ] && ! $user [ 'Role' ][ 'perm_site_admin' ]) {
$analystData [ $type ][ 'orgc_uuid' ] = $analystData [ $type ][ 'org_uuid' ]; // Only sync user can create analyst data on behalf of other users
}
} else {
if ( $analystData [ $type ][ 'Orgc' ][ 'uuid' ] != $user [ 'Organisation' ][ 'uuid' ] && ! $user [ 'Role' ][ 'perm_sync' ] && ! $user [ 'Role' ][ 'perm_site_admin' ]) {
$analystData [ $type ][ 'orgc_uuid' ] = $analystData [ $type ][ 'org_uuid' ]; // Only sync user can create analyst data on behalf of other users
}
}
if ( isset ( $analystData [ $type ][ 'orgc_uuid' ]) && $analystData [ $type ][ 'orgc_uuid' ] != $user [ 'Organisation' ][ 'uuid' ] && ! $user [ 'Role' ][ 'perm_sync' ] && ! $user [ 'Role' ][ 'perm_site_admin' ]) {
$analystData [ $type ][ 'orgc_uuid' ] = $analystData [ $type ][ 'org_uuid' ]; // Only sync user can create analyst data on behalf of other users
}
}
if ( ! Configure :: check ( 'MISP.enableOrgBlocklisting' ) || Configure :: read ( 'MISP.enableOrgBlocklisting' ) !== false ) {
$analystModel -> OrgBlocklist = ClassRegistry :: init ( 'OrgBlocklist' );
2024-02-14 09:48:11 +01:00
$orgcUUID = $analystData [ $type ][ 'Orgc' ][ 'uuid' ];
if ( $analystData [ $type ][ 'orgc_uuid' ] != 0 && $analystModel -> OrgBlocklist -> hasAny ( array ( 'OrgBlocklist.org_uuid' => $orgcUUID ))) {
$results [ 'errors' ][] = __ ( 'Organisation blocklisted (%s)' , $orgcUUID );
2024-02-01 14:24:41 +01:00
$results [ 'ignored' ] ++ ;
return $results ;
}
}
$analystData = $analystModel -> captureOrganisationAndSG ( $analystData , $type , $user );
if ( ! isset ( $analystData [ $type ][ 'distribution' ])) {
$analystData [ $type ][ 'distribution' ] = Configure :: read ( 'MISP.default_event_distribution' ); // use default event distribution
}
if ( $analystData [ $type ][ 'distribution' ] != 4 ) {
$analystData [ $type ][ 'sharing_group_id' ] = null ;
}
2024-02-14 11:22:43 +01:00
// Start saving from the leaf since to make sure child elements get saved even if the parent should not be saved (or updated due to locked or timestamp)
foreach ( self :: ANALYST_DATA_TYPES as $childType ) {
if ( ! empty ( $analystData [ $type ][ $childType ])) {
foreach ( $analystData [ $type ][ $childType ] as $childAnalystData ) {
$captureResult = $this -> { $childType } -> captureAnalystData ( $user , $childAnalystData , $fromPull , $orgUUId , $server );
$results [ 'imported' ] += $captureResult [ 'imported' ];
$results [ 'ignored' ] += $captureResult [ 'ignored' ];
$results [ 'failed' ] += $captureResult [ 'failed' ];
$results [ 'errors' ] = array_merge ( $results [ 'errors' ], $captureResult [ 'errors' ]);
}
}
}
2024-02-05 11:58:40 +01:00
$existingAnalystData = $analystModel -> find ( 'first' , [
'conditions' => [ " { $type } .uuid " => $analystData [ $type ][ 'uuid' ],],
]);
2024-02-01 14:24:41 +01:00
if ( empty ( $existingAnalystData )) {
unset ( $analystData [ $type ][ 'id' ]);
$analystModel -> create ();
$saveSuccess = $analystModel -> save ( $analystData );
2024-02-14 09:48:11 +01:00
$saveSuccess = true ;
2024-02-01 14:24:41 +01:00
} else {
if ( ! $existingAnalystData [ $type ][ 'locked' ] && empty ( $server [ 'Server' ][ 'internal' ])) {
$results [ 'errors' ][] = __ ( 'Blocked an edit to an analyst data that was created locally. This can happen if a synchronised analyst data that was created on this instance was modified by an administrator on the remote side.' );
$results [ 'failed' ] ++ ;
return $results ;
}
if ( $analystData [ $type ][ 'modified' ] > $existingAnalystData [ $type ][ 'modified' ]) {
$analystData [ $type ][ 'id' ] = $existingAnalystData [ $type ][ 'id' ];
$saveSuccess = $analystModel -> save ( $analystData );
} else {
$results [ 'errors' ][] = __ ( 'Remote version is not newer than local one for analyst data (%s)' , $analystData [ $type ][ 'uuid' ]);
$results [ 'ignored' ] ++ ;
return $results ;
}
}
if ( $saveSuccess ) {
$results [ 'imported' ] ++ ;
} else {
$results [ 'failed' ] ++ ;
foreach ( $analystModel -> validationErrors as $validationError ) {
$results [ 'errors' ][] = $validationError [ 0 ];
}
}
$results [ 'success' ] = $results [ 'imported' ] > 0 ;
return $results ;
}
public function captureOrganisationAndSG ( $element , $model , $user )
{
$this -> Event = ClassRegistry :: init ( 'Event' );
if ( isset ( $element [ $model ][ 'distribution' ]) && $element [ $model ][ 'distribution' ] == 4 ) {
$element [ $model ] = $this -> Event -> captureSGForElement ( $element [ $model ], $user );
}
// first we want to see how the creator organisation is encoded
// The options here are either by passing an organisation object along or simply passing a string along
if ( isset ( $element [ $model ][ 'Orgc' ])) {
$element [ $model ][ 'orgc_uuid' ] = $this -> Orgc -> captureOrg ( $element [ $model ][ 'Orgc' ], $user , false , true );
unset ( $element [ $model ][ 'Orgc' ]);
} else {
// Can't capture the Orgc, default to the current user
$element [ $model ][ 'orgc_uuid' ] = $user [ 'Organisation' ][ 'uuid' ];
}
return $element ;
}
/**
2024-02-05 15:18:07 +01:00
* Push Analyst Data to remote server . Collect elligible data locally and propose the list to the remote .
* Remote will then return the list of UUIDs it ' s willing to get . Then , upload these entries .
*
2024-01-31 15:10:08 +01:00
* @ param array $user
* @ param ServerSyncTool $serverSync
* @ return array
* @ throws Exception
*/
2024-02-05 15:18:07 +01:00
public function push ( array $user , ServerSyncTool $serverSync ) : array
2024-01-31 15:10:08 +01:00
{
$server = $serverSync -> server ();
if ( ! $server [ 'Server' ][ 'push_analyst_data' ]) {
return [];
}
2024-02-01 14:24:41 +01:00
$this -> Server = ClassRegistry :: init ( 'Server' );
2024-03-30 15:06:42 +01:00
$serverSync -> debug ( " Starting Analyst Data sync " );
2024-01-31 15:10:08 +01:00
2024-02-06 08:50:21 +01:00
$analystData = $this -> collectDataForPush ( $serverSync -> server ());
2024-02-01 14:24:41 +01:00
$keyedAnalystData = [];
foreach ( $analystData as $type => $entries ) {
foreach ( $entries as $entry ) {
$entry = $entry [ $type ];
$keyedAnalystData [ $type ][ $entry [ 'uuid' ]] = $entry [ 'modified' ];
}
}
if ( empty ( $analystData )) {
return [];
}
try {
$conditions = [];
2024-02-01 14:53:36 +01:00
foreach ( $keyedAnalystData as $type => $entry ) {
$conditions [ $type ] = array_keys ( $entry );
2024-02-01 14:24:41 +01:00
}
2024-02-05 15:18:07 +01:00
$analystDataToPush = $this -> identifyUUIDsForPush ( $serverSync , $analystData , $conditions );
2024-02-01 14:24:41 +01:00
} catch ( Exception $e ) {
$this -> logException ( " Could not get eligible Analyst Data IDs from server # { $server [ 'Server' ][ 'id' ] } for push. " , $e );
return [];
}
$successes = [];
2024-02-01 14:53:36 +01:00
foreach ( $analystDataToPush as $type => $entries ) {
2024-02-01 14:24:41 +01:00
foreach ( $entries as $entry ) {
2024-02-05 15:18:07 +01:00
$result = $this -> uploadEntryToServer ( $type , $entry , $server , $serverSync , $user );
2024-02-01 14:24:41 +01:00
if ( $result === 'Success' ) {
2024-02-01 14:53:36 +01:00
$successes [] = __ ( 'AnalystData %s' , $entry [ $type ][ 'uuid' ]);
2024-02-01 14:24:41 +01:00
}
}
}
return $successes ;
}
/**
* Collect elligible data to be pushed on a server
*
* @ param array $user
* @ return array
*/
2024-02-06 08:50:21 +01:00
public function collectDataForPush ( array $server ) : array
2024-02-01 14:24:41 +01:00
{
2024-02-06 08:50:21 +01:00
$sgIDs = $this -> collectValidSharingGroupIDs ( $server );
2024-02-01 14:24:41 +01:00
$options = [
'recursive' => - 1 ,
'conditions' => [
2024-02-06 08:50:21 +01:00
'OR' => [
[
'AND' => [
[ 'distribution >' => 0 ],
[ 'distribution <' => 4 ],
]
],
[
'AND' => [
'distribution' => 4 ,
'sharing_group_id' => $sgIDs ,
]
],
]
2024-02-01 14:24:41 +01:00
],
];
2024-02-06 08:50:21 +01:00
$dataForPush = $this -> getAllAnalystData ( 'all' , $options );
$this -> Event = ClassRegistry :: init ( 'Event' );
2024-02-29 11:18:06 +01:00
$SGModel = ClassRegistry :: init ( 'SharingGroup' );
$sgStore = [];
2024-02-06 08:50:21 +01:00
foreach ( $dataForPush as $type => $entries ) {
foreach ( $entries as $i => $analystData ) {
2024-02-29 11:18:06 +01:00
if ( isset ( $analystData [ $type ][ 'SharingGroup' ])) {
$sg_id = $analystData [ $type ][ 'SharingGroup' ][ 'id' ];
if ( ! isset ( $sgStore [ $sg_id ])) {
$sg = $SGModel -> find ( 'first' , [
'contain' => [
'SharingGroupServer' => [
'Server' => [
'fields' => [
'Server.id' ,
'Server.url' ,
'Server.remote_org_id'
]
]
],
'SharingGroupOrg' => [
'Organisation' => [
'fields' => [
'Organisation.id' ,
'Organisation.uuid'
]
]
],
'Organisation' => [
'fields' => [
'Organisation.id' ,
'Organisation.uuid'
]
]
],
'conditions' => [ 'SharingGroup.id' => $sg_id ]
]);
$temp = $sg [ 'SharingGroup' ];
$captureSGDataFields = [ 'Organisation' , 'SharingGroupOrg' , 'SharingGroupServer' ];
foreach ( $captureSGDataFields as $field ) {
$temp [ $field ] = $sg [ $field ];
}
$sgStore [ $sg_id ] = $temp ;
}
if ( isset ( $sgStore [ $analystData [ $type ][ 'SharingGroup' ][ 'id' ]])) {
$dataForPush [ $type ][ $i ][ $type ][ 'SharingGroup' ] = $sgStore [ $sg_id ];
}
}
if ( ! $this -> Event -> checkDistributionForPush ( $dataForPush [ $type ][ $i ], $server , $type )) {
2024-02-06 08:50:21 +01:00
unset ( $dataForPush [ $type ][ $i ]);
}
2024-02-06 10:13:16 +01:00
if ( ! $this -> isPushableForServerSyncRules ( $analystData [ $type ], $server )) {
unset ( $dataForPush [ $type ][ $i ]);
}
2024-02-06 08:50:21 +01:00
}
2024-02-29 11:18:06 +01:00
$dataForPush [ $type ] = array_values ( $dataForPush [ $type ]);
2024-02-06 08:50:21 +01:00
}
return $dataForPush ;
}
private function collectValidSharingGroupIDs ( array $server ) : array
{
$this -> SharingGroup = ClassRegistry :: init ( 'SharingGroup' );
$sgs = $this -> SharingGroup -> find ( 'all' , [
'recursive' => - 1 ,
'contain' => [ 'Organisation' , 'SharingGroupOrg' => [ 'Organisation' ], 'SharingGroupServer' ]
]);
$sgIDs = [];
foreach ( $sgs as $sg ) {
if ( $this -> SharingGroup -> checkIfServerInSG ( $sg , $server )) {
$sgIDs [] = $sg [ 'SharingGroup' ][ 'id' ];
}
}
if ( empty ( $sgIDs )) {
$sgIDs = [ - 1 ];
}
return $sgIDs ;
2024-02-01 14:24:41 +01:00
}
2024-02-06 10:13:16 +01:00
private function isPushableForServerSyncRules ( array $analystData , array $server ) : bool
{
$push_rules = json_decode ( $server [ 'Server' ][ 'push_rules' ], true );
if ( ! empty ( $push_rules [ 'orgs' ][ 'OR' ])) {
if ( ! in_array ( $analystData [ 'Orgc' ][ 'id' ], $push_rules [ 'orgs' ][ 'OR' ])) {
return false ;
}
}
if ( ! empty ( $push_rules [ 'orgs' ][ 'NOT' ])) {
if ( in_array ( $analystData [ 'Orgc' ][ 'id' ], $push_rules [ 'orgs' ][ 'NOT' ])) {
return false ;
}
}
return true ;
}
2024-02-05 15:18:07 +01:00
/**
* Get an array of analyst data that the remote is willing to get and returns analyst data that should be pushed .
* @ param ServerSyncTool $serverSync
* @ param array $localAnalystData
* @ param array $conditions
* @ return array
* @ throws HttpSocketHttpException
* @ throws HttpSocketJsonException
* @ throws JsonException
*/
public function identifyUUIDsForPush ( ServerSyncTool $serverSync , array $localAnalystData = [], array $conditions = []) : array
{
$this -> log ( " Fetching eligible analyst data from server # { $serverSync -> serverId () } for push: " . JsonTool :: encode ( $conditions ), LOG_INFO );
$candidates = [];
foreach ( $localAnalystData as $type => $entries ) {
foreach ( $entries as $entry ) {
$entry = $entry [ $type ];
$candidates [ $type ][ $entry [ 'uuid' ]] = $entry [ 'modified' ];
}
}
$remoteDataArray = $this -> proposeDataToRemote ( $serverSync , $candidates );
foreach ( $localAnalystData as $type => $entries ) {
foreach ( $entries as $i => $entry ) {
$entry = $entry [ $type ];
if ( ! isset ( $remoteDataArray [ $type ][ $entry [ 'uuid' ]])) {
unset ( $localAnalystData [ $type ][ $i ]);
}
}
}
return $localAnalystData ;
}
public function proposeDataToRemote ( ServerSyncTool $serverSync , array $candidates ) : array
{
$acceptedDataForPush = $this -> Server -> filterAnalystDataForPush ( $serverSync , $candidates );
return $acceptedDataForPush ;
}
2024-02-01 14:24:41 +01:00
public function filterAnalystDataForPush ( $allIncomingAnalystData ) : array
{
$validModels = [
'Note' => ClassRegistry :: init ( 'Note' ),
'Opinion' => ClassRegistry :: init ( 'Opinion' ),
'Relationship' => ClassRegistry :: init ( 'Relationship' ),
];
$allData = [ 'Note' => [], 'Opinion' => [], 'Relationship' => []];
foreach ( $allIncomingAnalystData as $model => $entries ) {
$incomingAnalystData = $entries ;
$incomingUuids = array_keys ( $entries );
$options = [
'conditions' => [ " { $model } .uuid " => $incomingUuids ],
'recursive' => - 1 ,
'fields' => [ 'uuid' , 'modified' , 'locked' ]
];
$analystData = $validModels [ $model ] -> find ( 'all' , $options );
foreach ( $analystData as $entry ) {
2024-02-05 15:42:02 +01:00
if ( empty ( $incomingAnalystData [ $entry [ $model ][ 'uuid' ]])) {
2024-02-01 14:24:41 +01:00
continue ;
}
2024-02-05 15:42:02 +01:00
if ( ! $this -> isCandidateValidForPush ( $incomingAnalystData [ $entry [ $model ][ 'uuid' ]], $entry [ $model ])) {
2024-02-01 14:24:41 +01:00
unset ( $incomingAnalystData [ $entry [ $model ][ 'uuid' ]]);
}
}
$allData [ $model ] = $incomingAnalystData ;
}
return $allData ;
}
2024-02-05 15:42:02 +01:00
private function isCandidateValidForPush ( $candidateModified , array $existingEntry ) : bool
{
if ( $existingEntry [ 'locked' ] == 0 ) {
return false ;
}
if ( strtotime ( $existingEntry [ 'modified' ]) >= strtotime ( $candidateModified )) {
return false ;
}
return true ;
}
2024-02-06 10:23:19 +01:00
public function indexMinimal ( array $user , $filters = []) : array
2024-02-05 08:52:37 +01:00
{
$options = [
'recursive' => - 1 ,
'conditions' => [
'AND' => [
$this -> buildConditions ( $user ),
2024-02-06 10:23:19 +01:00
'AND' => [ $filters ],
],
2024-02-05 08:52:37 +01:00
],
'fields' => [ 'uuid' , 'modified' , 'locked' ]
];
$tmp = $this -> getAllAnalystData ( 'all' , $options );
$allData = [];
foreach ( $tmp as $type => $entries ) {
foreach ( $entries as $i => $entry ) {
$entry = $entry [ $type ];
$allData [ $type ][ $entry [ 'uuid' ]] = $entry [ 'modified' ];
}
}
return $allData ;
}
2024-02-01 14:24:41 +01:00
/**
* getAllAnalystData Collect all analyst data regardless if they are notes , opinions or relationships
*
* @ param array $user
* @ return array
*/
public function getAllAnalystData ( $findType = 'all' , array $findOptions = []) : array
{
$allData = [];
2024-02-05 08:52:37 +01:00
$validModels = [
'Note' => ClassRegistry :: init ( 'Note' ),
'Opinion' => ClassRegistry :: init ( 'Opinion' ),
'Relationship' => ClassRegistry :: init ( 'Relationship' ),
];
2024-02-01 14:24:41 +01:00
foreach ( $validModels as $model ) {
$result = $model -> find ( $findType , $findOptions );
$allData [ $model -> alias ] = $result ;
}
return $allData ;
}
public function uploadEntryToServer ( $type , array $analystData , array $server , ServerSyncTool $serverSync , array $user )
{
$analystDataID = $analystData [ $type ][ 'id' ];
$analystData = $this -> prepareForPushToServer ( $type , $analystData , $server );
if ( is_numeric ( $analystData )) {
return $analystData ;
}
try {
if ( ! $serverSync -> isSupported ( ServerSyncTool :: PERM_SYNC ) || ! $serverSync -> isSupported ( ServerSyncTool :: PERM_ANALYST_DATA )) {
return __ ( 'The remote user does not have the permission to manipulate analyst data, the upload of the analyst data has been blocked.' );
}
$serverSync -> pushAnalystData ( $type , $analystData ) -> json ();
} catch ( Exception $e ) {
$title = __ ( 'Uploading AnalystData (%s::%s) to Server (%s)' , $type , $analystDataID , $server [ 'Server' ][ 'id' ]);
$this -> loadLog () -> createLogEntry ( $user , 'push' , 'AnalystData' , $analystDataID , $title , $e -> getMessage ());
$this -> logException ( " Could not push analyst data to remote server { $serverSync -> serverId () } " , $e );
return $e -> getMessage ();
}
return 'Success' ;
}
private function prepareForPushToServer ( $type , array $analystData , array $server )
{
if ( $analystData [ $type ][ 'distribution' ] == 4 ) {
if ( ! empty ( $analystData [ $type ][ 'SharingGroup' ][ 'SharingGroupServer' ])) {
$found = false ;
foreach ( $analystData [ $type ][ 'SharingGroup' ][ 'SharingGroupServer' ] as $sgs ) {
if ( $sgs [ 'server_id' ] == $server [ 'Server' ][ 'id' ]) {
$found = true ;
}
}
if ( ! $found ) {
return 403 ;
}
} elseif ( empty ( $analystData [ $type ][ 'SharingGroup' ][ 'roaming' ])) {
return 403 ;
}
}
$this -> Event = ClassRegistry :: init ( 'Event' );
if ( $this -> Event -> checkDistributionForPush ( $analystData , $server , $type )) {
return $this -> updateAnalystDataForSync ( $type , $analystData , $server );
}
return 403 ;
}
private function updateAnalystDataForSync ( $type , array $analystData , array $server ) : array
{
$this -> Event = ClassRegistry :: init ( 'Event' );
// cleanup the array from things we do not want to expose
foreach ([ 'id' ] as $field ) {
unset ( $analystData [ $type ][ $field ]);
}
// Add the local server to the list of instances in the SG
if ( isset ( $analystData [ $type ][ 'SharingGroup' ]) && isset ( $analystData [ $type ][ 'SharingGroup' ][ 'SharingGroupServer' ])) {
foreach ( $analystData [ $type ][ 'SharingGroup' ][ 'SharingGroupServer' ] as & $s ) {
if ( $s [ 'server_id' ] == 0 ) {
$s [ 'Server' ] = array (
'id' => 0 ,
'url' => $this -> Event -> __getAnnounceBaseurl (),
'name' => $this -> Event -> __getAnnounceBaseurl ()
);
}
}
}
2024-02-05 15:18:07 +01:00
$analystData [ $type ][ 'locked' ] = true ;
2024-02-01 14:24:41 +01:00
// Downgrade the event from connected communities to community only
if ( ! $server [ 'Server' ][ 'internal' ] && $analystData [ $type ][ 'distribution' ] == 2 ) {
$analystData [ $type ][ 'distribution' ] = 1 ;
}
return $analystData ;
2024-01-31 15:10:08 +01:00
}
2024-02-01 16:27:54 +01:00
2024-02-05 10:59:20 +01:00
/**
* Collect all UUIDs with their modified time on the remote side , then filter the list based on what we have locally .
* Afterward , iteratively pull what should be pulled .
*
* @ param array $user
* @ param ServerSyncTool $serverSync
2024-03-04 15:36:34 +01:00
* @ return int Number of saved analysis
2024-02-05 10:59:20 +01:00
*/
2024-02-01 16:27:54 +01:00
public function pull ( array $user , ServerSyncTool $serverSync )
{
2024-03-04 15:36:34 +01:00
if ( ! $serverSync -> isSupported ( ServerSyncTool :: PERM_ANALYST_DATA )) {
return 0 ;
}
2024-02-01 16:27:54 +01:00
$this -> Server = ClassRegistry :: init ( 'Server' );
try {
2024-02-06 11:15:05 +01:00
$filterRules = $this -> buildPullFilterRules ( $serverSync -> server ());
2024-02-06 11:34:10 +01:00
$remoteData = $serverSync -> fetchIndexMinimal ( $filterRules ) -> json ();
2024-02-01 16:27:54 +01:00
} catch ( Exception $e ) {
$this -> logException ( " Could not fetch analyst data IDs from server { $serverSync -> server ()[ 'Server' ][ 'name' ] } " , $e );
return 0 ;
}
2024-02-05 10:59:20 +01:00
$allRemoteUUIDs = [];
2024-02-15 15:21:00 +01:00
if ( empty ( $remoteData )) {
return 0 ;
}
2024-02-05 10:59:20 +01:00
foreach ( self :: ANALYST_DATA_TYPES as $type ) {
2024-02-29 11:18:06 +01:00
if ( isset ( $remoteData [ $type ])) {
$allRemoteUUIDs = array_merge ( $allRemoteUUIDs , array_keys ( $remoteData [ $type ]));
}
2024-02-05 10:59:20 +01:00
}
2024-02-01 16:27:54 +01:00
$localAnalystData = $this -> getAllAnalystData ( 'list' , [
2024-02-05 10:59:20 +01:00
'conditions' => [ 'uuid' => $allRemoteUUIDs ],
'fields' => [ 'uuid' , 'modified' ],
2024-02-01 16:27:54 +01:00
]);
2024-02-05 10:59:20 +01:00
$remoteUUIDsToFetch = [];
2024-02-01 16:27:54 +01:00
foreach ( $remoteData as $type => $remoteAnalystData ) {
2024-02-05 10:59:20 +01:00
foreach ( $remoteAnalystData as $remoteUUID => $remoteModified ) {
if ( ! isset ( $localAnalystData [ $type ][ $remoteUUID ])) {
$remoteUUIDsToFetch [ $type ][ $remoteUUID ] = $remoteModified ;
} elseif ( strtotime ( $localAnalystData [ $type ][ $remoteUUID ]) < strtotime ( $remoteModified )) {
$remoteUUIDsToFetch [ $type ][ $remoteUUID ] = $remoteModified ;
2024-02-01 16:27:54 +01:00
}
}
}
2024-02-05 10:59:20 +01:00
unset ( $remoteData , $allRemoteUUIDs , $localAnalystData );
2024-02-01 16:27:54 +01:00
2024-02-05 10:59:20 +01:00
if ( empty ( $remoteUUIDsToFetch )) {
2024-02-01 16:27:54 +01:00
return 0 ;
}
2024-03-04 15:36:34 +01:00
return $this -> pullInChunks ( $user , $remoteUUIDsToFetch , $serverSync );
2024-02-01 16:27:54 +01:00
}
2024-03-04 15:36:34 +01:00
private function pullInChunks ( array $user , array $analystDataUuids , ServerSyncTool $serverSync )
2024-02-01 16:27:54 +01:00
{
$saved = 0 ;
2024-02-05 10:59:20 +01:00
$serverOrgUUID = $this -> Org -> find ( 'first' , [
'recursive' => - 1 ,
'conditions' => [ 'id' => $serverSync -> server ()[ 'Server' ][ 'org_id' ]],
'fields' => [ 'id' , 'uuid' ]
])[ 'Organisation' ][ 'uuid' ];
foreach ( $analystDataUuids as $type => $entries ) {
$uuids = array_keys ( $entries );
if ( empty ( $uuids )) {
2024-02-01 16:27:54 +01:00
continue ;
}
2024-02-05 10:59:20 +01:00
foreach ( array_chunk ( $uuids , 100 ) as $uuidChunk ) {
try {
$chunkedAnalystData = $serverSync -> fetchAnalystData ( $type , $uuidChunk ) -> json ();
} catch ( Exception $e ) {
$this -> logException ( " Failed downloading the chunked analyst data from { $serverSync -> server ()[ 'Server' ][ 'name' ] } . " , $e );
continue ;
}
foreach ( $chunkedAnalystData as $analystData ) {
2024-02-05 11:58:40 +01:00
$analystData = $this -> updatePulledBeforeInsert ( $analystData , $type , $serverSync -> server (), $user , $serverSync -> pullRules ());
2024-02-05 10:59:20 +01:00
$savedResult = $this -> captureAnalystData ( $user , $analystData , true , $serverOrgUUID , $serverSync -> server ());
if ( $savedResult [ 'success' ]) {
$saved += $savedResult [ 'imported' ];
}
2024-02-01 16:27:54 +01:00
}
}
}
return $saved ;
}
2024-02-05 11:58:40 +01:00
private function updatePulledBeforeInsert ( array $analystData , $type , array $server , array $user , array $pullRules ) : array
{
$analystData [ $type ][ 'locked' ] = true ;
if ( empty ( Configure :: read ( 'MISP.host_org_id' )) || ! $server [ 'Server' ][ 'internal' ] || Configure :: read ( 'MISP.host_org_id' ) != $server [ 'Server' ][ 'org_id' ]) {
switch ( $analystData [ $type ][ 'distribution' ]) {
case 1 :
// if community only, downgrade to org only after pull
$analystData [ $type ][ 'distribution' ] = '0' ;
break ;
case 2 :
// if connected communities downgrade to community only
$analystData [ $type ][ 'distribution' ] = '1' ;
break ;
}
}
return $analystData ;
}
2024-02-06 11:15:05 +01:00
private function buildPullFilterRules ( array $server ) : array
{
2024-02-06 11:30:11 +01:00
$filterRules = [ 'orgc_name' => []];
2024-02-06 11:15:05 +01:00
$pullRules = $this -> jsonDecode ( $server [ 'Server' ][ 'pull_rules' ]);
if ( ! empty ( $pullRules [ 'orgs' ][ 'OR' ])) {
2024-02-06 11:30:11 +01:00
$filterRules [ 'orgc_name' ] = $pullRules [ 'orgs' ][ 'OR' ];
2024-02-06 11:15:05 +01:00
}
if ( ! empty ( $pullRules [ 'orgs' ][ 'NOT' ])) {
2024-02-06 11:30:11 +01:00
$filterRules [ 'orgc_name' ] = array_merge ( $filterRules [ 'orgc_name' ], array_map ( function ( $orgName ) {
return '!' . $orgName ;
}, $pullRules [ 'orgs' ][ 'NOT' ]));
2024-02-06 11:15:05 +01:00
}
return $filterRules ;
}
2024-01-04 10:12:47 +01:00
}