Twig, Tests, AES,
This commit is contained in:
143
test/AesStream.test.php
Normal file
143
test/AesStream.test.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
use Objects\AesStream;
|
||||
|
||||
class AesStreamTest extends PHPUnit\Framework\TestCase {
|
||||
|
||||
static string $TEMP_FILE;
|
||||
|
||||
public static function setUpBeforeClass(): void {
|
||||
AesStreamTest::$TEMP_FILE = tempnam(sys_get_temp_dir(), 'aesTest');
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void {
|
||||
unlink(AesStreamTest::$TEMP_FILE);
|
||||
}
|
||||
|
||||
public function testConstructorInvalidKey1() {
|
||||
$this->expectExceptionMessage("Invalid Key Size");
|
||||
$this->expectException(\Exception::class);
|
||||
new AesStream("", "");
|
||||
}
|
||||
|
||||
public function testConstructorInvalidKey2() {
|
||||
$this->expectExceptionMessage("Invalid Key Size");
|
||||
$this->expectException(\Exception::class);
|
||||
new AesStream(str_repeat("A",15), "");
|
||||
}
|
||||
|
||||
public function testConstructorInvalidKey3() {
|
||||
$this->expectExceptionMessage("Invalid Key Size");
|
||||
$this->expectException(\Exception::class);
|
||||
new AesStream(str_repeat("A",33), "");
|
||||
}
|
||||
|
||||
public function testConstructorInvalidIV1() {
|
||||
$this->expectExceptionMessage("Invalid IV Size");
|
||||
$this->expectException(\Exception::class);
|
||||
new AesStream(str_repeat("A",32), str_repeat("B", 17));
|
||||
}
|
||||
|
||||
public function testConstructorValid() {
|
||||
$key = str_repeat("A",32);
|
||||
$iv = str_repeat("B", 16);
|
||||
$aesStream = new AesStream($key, $iv);
|
||||
$this->assertInstanceOf(AesStream::class, $aesStream);
|
||||
$this->assertEquals($key, $aesStream->getKey());
|
||||
$this->assertEquals($iv, $aesStream->getIV());
|
||||
$this->assertEquals("aes-256-ctr", $aesStream->getCipherMode());
|
||||
}
|
||||
|
||||
private function getOutput(string $chunk, string &$data) {
|
||||
$data .= $chunk;
|
||||
}
|
||||
|
||||
public function testEncrypt() {
|
||||
$key = str_repeat("A", 32);
|
||||
$iv = str_repeat("B", 16);
|
||||
$aesStream = new AesStream($key, $iv);
|
||||
|
||||
$data = [
|
||||
"43" => "8c", # small block test 1 (1 byte)
|
||||
"abcd" => "6424", # small block test 2 (2 byte)
|
||||
"a37c599429cfdefde6546ad6d7082a" => "6c9539264abc8cae39308dbc86e768", # small block test 3 (15 byte)
|
||||
"43b3504077482bd9bf8c3c08ad3c937f" => "8c5a30f2143b798a60e8db62fcd3d1f7", # one block (16 byte)
|
||||
"9b241a3d7e9f03f6e66a8fa0cba3221008eda86f465e3fbfb0f3a4d3527cffb7"
|
||||
=> "54cd7a8f1dec51a5390e68ca9a4c60986aaafadd42b6960a09deedfa7f2cf1c3" # two blocks (16 byte)
|
||||
];
|
||||
|
||||
foreach ($data as $pt => $ct) {
|
||||
$output = "";
|
||||
file_put_contents(AesStreamTest::$TEMP_FILE, hex2bin($pt));
|
||||
$aesStream->setInputFile(AesStreamTest::$TEMP_FILE);
|
||||
$aesStream->setOutput(function($chunk) use (&$output) { $this->getOutput($chunk, $output); });
|
||||
$aesStream->start();
|
||||
$this->assertEquals($ct, bin2hex($output), $ct . " != " . bin2hex($output));
|
||||
}
|
||||
}
|
||||
|
||||
private function openssl(AesStream $aesStream) {
|
||||
// check if openssl util produce the same output
|
||||
$cmd = ["/usr/bin/openssl", $aesStream->getCipherMode(), "-K", bin2hex($aesStream->getKey()), "-iv", bin2hex($aesStream->getIV()), "-in", AesStreamTest::$TEMP_FILE];
|
||||
$proc = proc_open($cmd, [1 => ["pipe", "w"]], $pipes);
|
||||
$this->assertTrue(is_resource($proc));
|
||||
$this->assertTrue(is_resource($pipes[1]));
|
||||
$output = stream_get_contents($pipes[1]);
|
||||
proc_close($proc);
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function testEncryptDecrypt($key, $iv, $inputData) {
|
||||
$aesStream = new AesStream($key, $iv);
|
||||
$inputSize = strlen($inputData);
|
||||
file_put_contents(AesStreamTest::$TEMP_FILE, $inputData);
|
||||
|
||||
$output = "";
|
||||
$aesStream->setInputFile(AesStreamTest::$TEMP_FILE);
|
||||
$aesStream->setOutput(function($chunk) use (&$output) { $this->getOutput($chunk, $output); });
|
||||
$aesStream->start();
|
||||
|
||||
$this->assertEquals($inputSize, strlen($output));
|
||||
$this->assertNotEquals($inputData, $output);
|
||||
|
||||
// check if openssl util produce the same output
|
||||
$this->assertEquals($this->openssl($aesStream), $output);
|
||||
|
||||
file_put_contents(AesStreamTest::$TEMP_FILE, $output);
|
||||
$output = "";
|
||||
$aesStream->setInputFile(AesStreamTest::$TEMP_FILE);
|
||||
$aesStream->setOutput(function($chunk) use (&$output) { $this->getOutput($chunk, $output); });
|
||||
$aesStream->start();
|
||||
$this->assertEquals($inputData, $output);
|
||||
|
||||
// check if openssl util produce the same output
|
||||
$this->assertEquals($this->openssl($aesStream), $output);
|
||||
}
|
||||
|
||||
public function testEncryptDecryptRandom() {
|
||||
$chunkSize = 65536;
|
||||
$key = random_bytes(32);
|
||||
$iv = random_bytes(16);
|
||||
$inputSize = 10 * $chunkSize;
|
||||
$inputData = random_bytes($inputSize);
|
||||
$this->testEncryptDecrypt($key, $iv, $inputData);
|
||||
}
|
||||
|
||||
public function testEncryptDecryptLargeIV() {
|
||||
$chunkSize = 65536;
|
||||
$key = random_bytes(32);
|
||||
$iv = hex2bin(str_repeat("FF", 16));
|
||||
$inputSize = 10 * $chunkSize;
|
||||
$inputData = random_bytes($inputSize);
|
||||
$this->testEncryptDecrypt($key, $iv, $inputData);
|
||||
}
|
||||
|
||||
public function testEncryptDecryptZeroIV() {
|
||||
$chunkSize = 65536;
|
||||
$key = random_bytes(32);
|
||||
$iv = hex2bin(str_repeat("00", 16));
|
||||
$inputSize = 10 * $chunkSize;
|
||||
$inputData = random_bytes($inputSize);
|
||||
$this->testEncryptDecrypt($key, $iv, $inputData);
|
||||
}
|
||||
}
|
||||
108
test/Parameter.test.php
Normal file
108
test/Parameter.test.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
use Api\Parameter\ArrayType;
|
||||
use Api\Parameter\StringType;
|
||||
use Api\Parameter\Parameter;
|
||||
|
||||
class ParameterTest extends \PHPUnit\Framework\TestCase {
|
||||
|
||||
public function testStringType() {
|
||||
|
||||
// test various string sizes
|
||||
$unlimited = new StringType("test_unlimited");
|
||||
$this->assertTrue($unlimited->parseParam(str_repeat("A", 1024)));
|
||||
|
||||
$empty = new StringType("test_empty", 0);
|
||||
$this->assertTrue($empty->parseParam(""));
|
||||
$this->assertTrue($empty->parseParam("A"));
|
||||
|
||||
$one = new StringType("test_one", 1);
|
||||
$this->assertTrue($one->parseParam(""));
|
||||
$this->assertTrue($one->parseParam("A"));
|
||||
$this->assertFalse($one->parseParam("AB"));
|
||||
|
||||
$randomSize = rand(1, 64);
|
||||
$random = new StringType("test_empty", $randomSize);
|
||||
$data = str_repeat("A", $randomSize);
|
||||
$this->assertTrue($random->parseParam(""));
|
||||
$this->assertTrue($random->parseParam("A"));
|
||||
$this->assertTrue($random->parseParam($data));
|
||||
$this->assertEquals($data, $random->value);
|
||||
|
||||
// test data types
|
||||
$this->assertFalse($random->parseParam(null));
|
||||
$this->assertFalse($random->parseParam(1));
|
||||
$this->assertFalse($random->parseParam(2.5));
|
||||
$this->assertFalse($random->parseParam(true));
|
||||
$this->assertFalse($random->parseParam(false));
|
||||
$this->assertFalse($random->parseParam(["key" => 1]));
|
||||
}
|
||||
|
||||
public function testArrayType() {
|
||||
|
||||
// int array type
|
||||
$arrayType = new ArrayType("int_array", Parameter::TYPE_INT);
|
||||
$this->assertTrue($arrayType->parseParam([1,2,3]));
|
||||
$this->assertTrue($arrayType->parseParam([1]));
|
||||
$this->assertTrue($arrayType->parseParam(["1"]));
|
||||
$this->assertTrue($arrayType->parseParam([1.0]));
|
||||
$this->assertTrue($arrayType->parseParam([]));
|
||||
$this->assertTrue($arrayType->parseParam(["1.0"]));
|
||||
$this->assertFalse($arrayType->parseParam([1.2]));
|
||||
$this->assertFalse($arrayType->parseParam(["1.5"]));
|
||||
$this->assertFalse($arrayType->parseParam([true]));
|
||||
$this->assertFalse($arrayType->parseParam(1));
|
||||
|
||||
// optional single value
|
||||
$arrayType = new ArrayType("int_array_single", Parameter::TYPE_INT, true);
|
||||
$this->assertTrue($arrayType->parseParam(1));
|
||||
|
||||
// mixed values
|
||||
$arrayType = new ArrayType("mixed_array", Parameter::TYPE_MIXED);
|
||||
$this->assertTrue($arrayType->parseParam([1, 2.5, "test", false]));
|
||||
}
|
||||
|
||||
public function testParseType() {
|
||||
// int
|
||||
$this->assertEquals(Parameter::TYPE_INT, Parameter::parseType(1));
|
||||
$this->assertEquals(Parameter::TYPE_INT, Parameter::parseType(1.0));
|
||||
$this->assertEquals(Parameter::TYPE_INT, Parameter::parseType("1"));
|
||||
$this->assertEquals(Parameter::TYPE_INT, Parameter::parseType("1.0"));
|
||||
|
||||
// array
|
||||
$this->assertEquals(Parameter::TYPE_ARRAY, Parameter::parseType([1, true]));
|
||||
|
||||
// float
|
||||
$this->assertEquals(Parameter::TYPE_FLOAT, Parameter::parseType(1.5));
|
||||
$this->assertEquals(Parameter::TYPE_FLOAT, Parameter::parseType(1.234e2));
|
||||
$this->assertEquals(Parameter::TYPE_FLOAT, Parameter::parseType("1.75"));
|
||||
|
||||
// boolean
|
||||
$this->assertEquals(Parameter::TYPE_BOOLEAN, Parameter::parseType(true));
|
||||
$this->assertEquals(Parameter::TYPE_BOOLEAN, Parameter::parseType(false));
|
||||
$this->assertEquals(Parameter::TYPE_BOOLEAN, Parameter::parseType("true"));
|
||||
$this->assertEquals(Parameter::TYPE_BOOLEAN, Parameter::parseType("false"));
|
||||
|
||||
// date
|
||||
$this->assertEquals(Parameter::TYPE_DATE, Parameter::parseType("2021-11-13"));
|
||||
$this->assertEquals(Parameter::TYPE_STRING, Parameter::parseType("2021-13-11")); # invalid date
|
||||
|
||||
// time
|
||||
$this->assertEquals(Parameter::TYPE_TIME, Parameter::parseType("10:11:12"));
|
||||
$this->assertEquals(Parameter::TYPE_STRING, Parameter::parseType("25:11:12")); # invalid time
|
||||
|
||||
// datetime
|
||||
$this->assertEquals(Parameter::TYPE_DATE_TIME, Parameter::parseType("2021-11-13 10:11:12"));
|
||||
$this->assertEquals(Parameter::TYPE_STRING, Parameter::parseType("2021-13-13 10:11:12")); # invalid date
|
||||
$this->assertEquals(Parameter::TYPE_STRING, Parameter::parseType("2021-13-11 10:61:12")); # invalid time
|
||||
|
||||
// email
|
||||
$this->assertEquals(Parameter::TYPE_EMAIL, Parameter::parseType("a@b.com"));
|
||||
$this->assertEquals(Parameter::TYPE_EMAIL, Parameter::parseType("test.123@example.com"));
|
||||
$this->assertEquals(Parameter::TYPE_STRING, Parameter::parseType("@example.com")); # invalid email
|
||||
$this->assertEquals(Parameter::TYPE_STRING, Parameter::parseType("test@")); # invalid email
|
||||
|
||||
// string, everything else
|
||||
$this->assertEquals(Parameter::TYPE_STRING, Parameter::parseType("test"));
|
||||
}
|
||||
}
|
||||
192
test/Request.test.php
Normal file
192
test/Request.test.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
use Api\Request;
|
||||
use Configuration\Configuration;
|
||||
use Objects\User;
|
||||
|
||||
function __new_header_impl(string $line) {
|
||||
if (preg_match("/^HTTP\/([0-9.]+) (\d+) (.*)$/", $line, $m)) {
|
||||
RequestTest::$SENT_STATUS_CODE = intval($m[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
$key = $line;
|
||||
$value = "";
|
||||
$index = strpos($key, ": ");
|
||||
if ($index !== false) {
|
||||
$key = substr($line, 0, $index);
|
||||
$value = substr($line, $index + 2);
|
||||
}
|
||||
|
||||
RequestTest::$SENT_HEADERS[$key] = $value;
|
||||
}
|
||||
|
||||
function __new_http_response_code_impl(int $code) {
|
||||
RequestTest::$SENT_STATUS_CODE = $code;
|
||||
}
|
||||
|
||||
function __new_die_impl($content) {
|
||||
RequestTest::$SENT_CONTENT = $content;
|
||||
}
|
||||
|
||||
class RequestTest extends \PHPUnit\Framework\TestCase {
|
||||
|
||||
const FUNCTION_OVERRIDES = ["header", "http_response_code"];
|
||||
static User $USER;
|
||||
static User $USER_LOGGED_IN;
|
||||
|
||||
static ?string $SENT_CONTENT;
|
||||
static array $SENT_HEADERS;
|
||||
static ?int $SENT_STATUS_CODE;
|
||||
|
||||
public static function setUpBeforeClass(): void {
|
||||
|
||||
$config = new Configuration();
|
||||
RequestTest::$USER = new User($config);
|
||||
RequestTest::$USER_LOGGED_IN = new User($config);
|
||||
if (!RequestTest::$USER->getSQL() || !RequestTest::$USER->getSQL()->isConnected()) {
|
||||
throw new Exception("Could not establish database connection");
|
||||
} else {
|
||||
RequestTest::$USER->setLanguage(\Objects\Language::DEFAULT_LANGUAGE());
|
||||
}
|
||||
|
||||
if (!function_exists("runkit7_function_rename") || !function_exists("runkit7_function_remove")) {
|
||||
throw new Exception("Request Unit Test requires runkit7 extension");
|
||||
}
|
||||
|
||||
if (ini_get("runkit.internal_override") !== "1") {
|
||||
throw new Exception("Request Unit Test requires runkit7 with internal_override enabled to function properly");
|
||||
}
|
||||
|
||||
foreach (self::FUNCTION_OVERRIDES as $functionName) {
|
||||
runkit7_function_rename($functionName, "__orig_${functionName}_impl");
|
||||
runkit7_function_rename("__new_${functionName}_impl", $functionName);
|
||||
}
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void {
|
||||
RequestTest::$USER->getSQL()->close();
|
||||
foreach (self::FUNCTION_OVERRIDES as $functionName) {
|
||||
runkit7_function_remove($functionName);
|
||||
runkit7_function_rename("__orig_${functionName}_impl", $functionName);
|
||||
}
|
||||
}
|
||||
|
||||
private function simulateRequest(Request $request, string $method, array $get = [], array $post = [], array $headers = []): bool {
|
||||
|
||||
if (!is_cli()) {
|
||||
self::throwException(new \Exception("Cannot simulate request outside cli"));
|
||||
}
|
||||
|
||||
$_SERVER = [];
|
||||
$_SERVER["REQUEST_METHOD"] = $method;
|
||||
self::$SENT_HEADERS = [];
|
||||
self::$SENT_STATUS_CODE = null;
|
||||
self::$SENT_CONTENT = null;
|
||||
|
||||
foreach ($headers as $key => $value) {
|
||||
$key = "HTTP_" . preg_replace("/\s/", "_", strtoupper($key));
|
||||
$_SERVER[$key] = $value;
|
||||
}
|
||||
|
||||
$_GET = $get;
|
||||
$_POST = $post;
|
||||
|
||||
return $request->execute();
|
||||
}
|
||||
|
||||
public function testAllMethods() {
|
||||
// all methods allowed
|
||||
$allMethodsAllowed = new RequestAllMethods(RequestTest::$USER, true);
|
||||
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "GET"), $allMethodsAllowed->getLastError());
|
||||
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "POST"), $allMethodsAllowed->getLastError());
|
||||
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "PUT"), $allMethodsAllowed->getLastError());
|
||||
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "DELETE"), $allMethodsAllowed->getLastError());
|
||||
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "OPTIONS"), $allMethodsAllowed->getLastError());
|
||||
$this->assertEquals(204, self::$SENT_STATUS_CODE);
|
||||
$this->assertEquals(["Allow" => "OPTIONS, GET, POST"], self::$SENT_HEADERS);
|
||||
}
|
||||
|
||||
public function testOnlyPost() {
|
||||
// only post allowed
|
||||
$onlyPostAllowed = new RequestOnlyPost(RequestTest::$USER, true);
|
||||
$this->assertFalse($this->simulateRequest($onlyPostAllowed, "GET"));
|
||||
$this->assertEquals("This method is not allowed", $onlyPostAllowed->getLastError(), $onlyPostAllowed->getLastError());
|
||||
$this->assertEquals(405, self::$SENT_STATUS_CODE);
|
||||
$this->assertTrue($this->simulateRequest($onlyPostAllowed, "POST"), $onlyPostAllowed->getLastError());
|
||||
$this->assertTrue($this->simulateRequest($onlyPostAllowed, "OPTIONS"), $onlyPostAllowed->getLastError());
|
||||
$this->assertEquals(204, self::$SENT_STATUS_CODE);
|
||||
$this->assertEquals(["Allow" => "OPTIONS, POST"], self::$SENT_HEADERS);
|
||||
}
|
||||
|
||||
public function testPrivate() {
|
||||
// private method
|
||||
$privateExternal = new RequestPrivate(RequestTest::$USER, true);
|
||||
$this->assertFalse($this->simulateRequest($privateExternal, "GET"));
|
||||
$this->assertEquals("This function is private.", $privateExternal->getLastError());
|
||||
$this->assertEquals(403, self::$SENT_STATUS_CODE);
|
||||
|
||||
$privateInternal = new RequestPrivate(RequestTest::$USER, false);
|
||||
$this->assertTrue($privateInternal->execute());
|
||||
}
|
||||
|
||||
public function testDisabled() {
|
||||
// disabled method
|
||||
$disabledMethod = new RequestDisabled(RequestTest::$USER, true);
|
||||
$this->assertFalse($this->simulateRequest($disabledMethod, "GET"));
|
||||
$this->assertEquals("This function is currently disabled.", $disabledMethod->getLastError(), $disabledMethod->getLastError());
|
||||
$this->assertEquals(503, self::$SENT_STATUS_CODE);
|
||||
}
|
||||
|
||||
public function testLoginRequired() {
|
||||
$loginRequired = new RequestLoginRequired(RequestTest::$USER, true);
|
||||
$this->assertFalse($this->simulateRequest($loginRequired, "GET"));
|
||||
$this->assertEquals("You are not logged in.", $loginRequired->getLastError(), $loginRequired->getLastError());
|
||||
$this->assertEquals(401, self::$SENT_STATUS_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TestRequest extends Request {
|
||||
public function __construct(User $user, bool $externalCall = false, $params = []) {
|
||||
parent::__construct($user, $externalCall, $params);
|
||||
}
|
||||
|
||||
protected function _die(string $data = ""): bool {
|
||||
__new_die_impl($data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class RequestAllMethods extends TestRequest {
|
||||
public function __construct(User $user, bool $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, []);
|
||||
}
|
||||
}
|
||||
|
||||
class RequestOnlyPost extends TestRequest {
|
||||
public function __construct(User $user, bool $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, []);
|
||||
$this->forbidMethod("GET");
|
||||
}
|
||||
}
|
||||
|
||||
class RequestPrivate extends TestRequest {
|
||||
public function __construct(User $user, bool $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, []);
|
||||
$this->isPublic = false;
|
||||
}
|
||||
}
|
||||
|
||||
class RequestDisabled extends TestRequest {
|
||||
public function __construct(User $user, bool $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, []);
|
||||
$this->isDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
class RequestLoginRequired extends TestRequest {
|
||||
public function __construct(User $user, bool $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, []);
|
||||
$this->loginRequired = true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user