mirror of https://github.com/MISP/MISP
new: [EventLock] creation of EventLockBehavior (#9687)
parent
808f3938fb
commit
06332c7511
|
@ -0,0 +1,204 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Model\Behavior;
|
||||||
|
|
||||||
|
use App\Lib\Tools\RedisTool;
|
||||||
|
use Cake\ORM\Behavior;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class EventLockBehavior extends Behavior
|
||||||
|
{
|
||||||
|
// In seconds
|
||||||
|
public const DEFAULT_TTL = 900,
|
||||||
|
PREFIX = 'misp:event_lock:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $user user array
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @return bool True if insert was successful.
|
||||||
|
*/
|
||||||
|
public function insertLock(array $user, int $eventId)
|
||||||
|
{
|
||||||
|
return $this->insertLockToRedis($eventId, "user:{$user['id']}", [
|
||||||
|
'type' => 'user',
|
||||||
|
'timestamp' => time(),
|
||||||
|
'User' => [
|
||||||
|
'id' => $user['id'],
|
||||||
|
'org_id' => $user['org_id'],
|
||||||
|
'email' => $user['email'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @param int $jobId job ID to insert
|
||||||
|
* @return bool True if insert was successful.
|
||||||
|
*/
|
||||||
|
public function insertLockBackgroundJob(int $eventId, int $jobId)
|
||||||
|
{
|
||||||
|
return $this->insertLockToRedis($eventId, "job:$jobId", [
|
||||||
|
'type' => 'job',
|
||||||
|
'timestamp' => time(),
|
||||||
|
'job_id' => $jobId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @param array $user user array
|
||||||
|
* @return int|null Lock ID
|
||||||
|
*/
|
||||||
|
public function insertLockApi(int $eventId, array $user)
|
||||||
|
{
|
||||||
|
$apiLockId = mt_rand();
|
||||||
|
if (
|
||||||
|
$this->insertLockToRedis(
|
||||||
|
$eventId,
|
||||||
|
"api:{$user['id']}:$apiLockId",
|
||||||
|
[
|
||||||
|
'type' => 'api',
|
||||||
|
'user_id' => $user['id'],
|
||||||
|
'timestamp' => time(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return $apiLockId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @param array $user user array
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteLock(int $eventId, array $user)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$redis = RedisTool::init();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleted = $redis->hdel(self::PREFIX . $eventId, "user:{$user['id']}");
|
||||||
|
|
||||||
|
return $deleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use deleteLockApi instead
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @param int $apiLockId api lock ID
|
||||||
|
* @param array $user user array
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteApiLock(int $eventId, int $apiLockId, array $user)
|
||||||
|
{
|
||||||
|
return $this->deleteLockApi($eventId, $apiLockId, $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @param int $apiLockId api lock ID
|
||||||
|
* @param array $user user array
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteLockApi(int $eventId, int $apiLockId, array $user)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$redis = RedisTool::init();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleted = $redis->hdel(self::PREFIX . $eventId, "api:{$user['id']}:$apiLockId");
|
||||||
|
|
||||||
|
return $deleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use deleteLockBackgroundJob instead
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @param int $jobId job ID
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteBackgroundJobLock(int $eventId, int $jobId)
|
||||||
|
{
|
||||||
|
return $this->deleteLockBackgroundJob($eventId, $jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @param int $jobId job ID
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteLockBackgroundJob(int $eventId, int $jobId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$redis = RedisTool::init();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleted = $redis->hDel(self::PREFIX . $eventId, "job:$jobId");
|
||||||
|
|
||||||
|
return $deleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $user user array
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @return array[]
|
||||||
|
* @throws \JsonException
|
||||||
|
*/
|
||||||
|
public function checkLock(array $user, int $eventId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$redis = RedisTool::init();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$keys = $redis->hGetAll(self::PREFIX . $eventId);
|
||||||
|
if (empty($keys)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = [];
|
||||||
|
$now = time();
|
||||||
|
foreach ($keys as $value) {
|
||||||
|
$value = RedisTool::deserialize($value);
|
||||||
|
if ($value['timestamp'] + self::DEFAULT_TTL > $now) {
|
||||||
|
$output[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $eventId event ID
|
||||||
|
* @param string $lockId lock ID
|
||||||
|
* @param array $data data to insert
|
||||||
|
* @return bool
|
||||||
|
* @throws \JsonException
|
||||||
|
* @throws \RedisException
|
||||||
|
*/
|
||||||
|
private function insertLockToRedis(int $eventId, $lockId, array $data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$redis = RedisTool::init();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pipeline = $redis->pipeline();
|
||||||
|
$pipeline->hSet(self::PREFIX . $eventId, $lockId, RedisTool::serialize($data));
|
||||||
|
$pipeline->expire(self::PREFIX . $eventId, self::DEFAULT_TTL); // prolong TTL
|
||||||
|
|
||||||
|
return $pipeline->exec()[0] !== false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Test\TestCase\Model\Behavior;
|
||||||
|
|
||||||
|
use App\Model\Behavior\EventLockBehavior;
|
||||||
|
use App\Test\Fixture\EventsFixture;
|
||||||
|
use App\Test\Fixture\OrganisationsFixture;
|
||||||
|
use App\Test\Fixture\UsersFixture;
|
||||||
|
use Cake\ORM\Table;
|
||||||
|
use Cake\TestSuite\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App\Model\Behavior\EventLockBehavior Test Case
|
||||||
|
*/
|
||||||
|
class EventLockBehaviorTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test subject
|
||||||
|
*
|
||||||
|
* @var \App\Model\Behavior\EventLockBehavior
|
||||||
|
*/
|
||||||
|
protected $EventLockBehavior;
|
||||||
|
protected $eventId;
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setUp method
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$table = new Table();
|
||||||
|
$this->EventLockBehavior = new EventLockBehavior($table);
|
||||||
|
|
||||||
|
$this->user = [
|
||||||
|
'id' => UsersFixture::USER_REGULAR_USER_ID,
|
||||||
|
'org_id' => OrganisationsFixture::ORGANISATION_A_ID,
|
||||||
|
'email' => UsersFixture::USER_REGULAR_USER_EMAIL,
|
||||||
|
];
|
||||||
|
$this->eventId = EventsFixture::EVENT_1_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tearDown method
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
unset($this->EventLockBehavior);
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test insertLockBackgroundJob method
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @uses \App\Model\Behavior\EventLockBehavior::insertLockBackgroundJob()
|
||||||
|
* @uses \App\Model\Behavior\EventLockBehavior::deleteLockBackgroundJob()
|
||||||
|
*/
|
||||||
|
public function testInsertDeleteLockBackgroundJob(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$jobId = 1;
|
||||||
|
$result = $this->EventLockBehavior->insertLockBackgroundJob($this->eventId, $jobId);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
} finally {
|
||||||
|
$result = $this->EventLockBehavior->deleteLockBackgroundJob($this->eventId, $jobId);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test insertLockApi, deleteApiLock methods
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @uses \App\Model\Behavior\EventLockBehavior::insertLockApi()
|
||||||
|
* @uses \App\Model\Behavior\EventLockBehavior::deleteLockApi()
|
||||||
|
*/
|
||||||
|
public function testInsertDeleteLockApi(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$apiLockId = $this->EventLockBehavior->insertLockApi($this->eventId, $this->user);
|
||||||
|
$this->assertNotEmpty($apiLockId);
|
||||||
|
} finally {
|
||||||
|
$output = $this->EventLockBehavior->deleteLockApi($this->eventId, $apiLockId, $this->user);
|
||||||
|
$this->assertTrue($output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test insertLock, checkLock, deleteLock methods
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @uses \App\Model\Behavior\EventLockBehavior::insertLock()
|
||||||
|
* @uses \App\Model\Behavior\EventLockBehavior::checkLock()
|
||||||
|
* @uses \App\Model\Behavior\EventLockBehavior::deleteLock()
|
||||||
|
*/
|
||||||
|
public function testInsertCheckDeleteLock(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$result = $this->EventLockBehavior->insertLock($this->user, $this->eventId);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
$output = $this->EventLockBehavior->checkLock($this->user, $this->eventId);
|
||||||
|
$this->assertNotEmpty($output);
|
||||||
|
} finally {
|
||||||
|
$result = $this->EventLockBehavior->deleteLock($this->eventId, $this->user);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue