chg: [API] Add support for ETag checking

pull/8157/head
Jakub Onderka 2022-02-22 20:58:52 +01:00
parent 9582d59563
commit 6a3253bd2e
3 changed files with 47 additions and 1 deletions

View File

@ -601,6 +601,13 @@ class RestResponseComponent extends Component
}
if ($response instanceof TmpFileTool) {
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$etag = '"' . $response->hash('sha1') . '"';
if ($_SERVER['HTTP_IF_NONE_MATCH'] === $etag) {
return new CakeResponse(['status' => 304]);
}
$headers['ETag'] = $etag;
}
if ($this->signContents) {
$this->CryptographicKey = ClassRegistry::init('CryptographicKey');
$data = $response->intoString();
@ -612,7 +619,16 @@ class RestResponseComponent extends Component
$cakeResponse->file($response);
}
} else {
$cakeResponse = new CakeResponse(array('body' => $response, 'status' => $code, 'type' => $type));
// Check if resource was changed when `If-None-Match` header is send and return 304 Not Modified
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$etag = '"' . sha1($response) . '"';
if ($_SERVER['HTTP_IF_NONE_MATCH'] === $etag) {
return new CakeResponse(['status' => 304]);
}
// Generate etag just when HTTP_IF_NONE_MATCH is set
$headers['ETag'] = $etag;
}
$cakeResponse = new CakeResponse(['body' => $response, 'status' => $code, 'type' => $type]);
if ($this->signContents) {
$headers['x-pgp-signature'] = base64_encode($this->CryptographicKey->signWithInstanceKey($response));
}

View File

@ -192,6 +192,19 @@ class TmpFileTool
return fstat($this->tmpfile)['size'];
}
/**
* @param string $algo
* @return string
* @throws Exception
*/
public function hash($algo)
{
$this->rewind();
$hash = hash_init($algo);
hash_update_stream($hash, $this->tmpfile);
return hash_final($hash);
}
/**
* @return string
* @throws Exception

View File

@ -4,6 +4,7 @@ import json
import uuid
import subprocess
import unittest
import requests
from xml.etree import ElementTree as ET
from io import BytesIO
import urllib3 # type: ignore
@ -746,6 +747,22 @@ class TestComprehensive(unittest.TestCase):
self.assertEqual(200, response.status_code, response)
response.json()
def test_etag(self):
headers = {
'Authorization': self.admin_misp_connector.key,
'Accept': 'application/json',
'User-Agent': 'PyMISP',
'If-None-Match': '',
}
response = requests.get(self.admin_misp_connector.root_url + '/attributes/describeTypes.json', headers=headers)
self.assertEqual(200, response.status_code)
self.assertIn('Etag', response.headers)
self.assertTrue(len(response.headers['Etag']) > 0, response.headers['Etag'])
headers['If-None-Match'] = response.headers['Etag']
response = requests.get(self.admin_misp_connector.root_url + '/attributes/describeTypes.json', headers=headers)
self.assertEqual(304, response.status_code, response.headers)
def _search(self, query: dict):
response = self.admin_misp_connector._prepare_request('POST', 'events/restSearch', data=query)
response = self.admin_misp_connector._check_response(response)