add more validations, handle invalid json payloads
continuous-integration/drone/push Build is passing Details

pull/8/head
Override-6 1 year ago
parent b08e761abb
commit 6ee05c7916
Signed by untrusted user who does not match committer: maxime.batista
GPG Key ID: 8002CC4B4DD9ECA5

@ -25,7 +25,7 @@ class APITacticController {
public function updateName(int $tactic_id): HttpResponse {
return Control::runChecked([
"name" => [Validators::userString(32)]
"name" => [Validators::lenBetween(1, 50), Validators::nameWithSpaces()]
], function (HttpRequest $request) use ($tactic_id) {
$this->model->updateName($tactic_id, $request["name"]);
return HttpResponse::fromCode(HttpCodes::OK);
@ -34,7 +34,7 @@ class APITacticController {
public function newTactic(): HttpResponse {
return Control::runChecked([
"name" => [Validators::userString(32)]
"name" => [Validators::lenBetween(1, 50), Validators::nameWithSpaces()]
], function (HttpRequest $request) {
$tactic = $this->model->makeNew($request["name"]);
$id = $tactic->getId();

@ -6,6 +6,7 @@ use App\Http\HttpCodes;
use App\Http\HttpRequest;
use App\Http\HttpResponse;
use App\Http\JsonHttpResponse;
use App\Validation\ValidationFail;
class Control {
@ -18,8 +19,11 @@ class Control {
*/
public static function runChecked(array $schema, callable $run): HttpResponse {
$request_body = file_get_contents('php://input');
$payload = get_object_vars(json_decode($request_body));
$payload_obj = json_decode($request_body);
if (!$payload_obj instanceof \stdClass) {
return new JsonHttpResponse([new ValidationFail("bad-payload", "request body is not a valid json object"), HttpCodes::BAD_REQUEST]);
}
$payload = get_object_vars($payload_obj);
return self::runCheckedFrom($payload, $schema, $run);
}

@ -4,6 +4,7 @@ namespace App\Controller;
use App\Data\TacticInfo;
use App\Http\HttpCodes;
use App\Http\HttpRequest;
use App\Http\HttpResponse;
use App\Http\JsonHttpResponse;
use App\Http\ViewHttpResponse;

@ -32,12 +32,12 @@ class SampleFormController {
private function submitForm(array $form, callable $response): HttpResponse {
return Control::runCheckedFrom($form, [
"name" => [Validators::userString(32)],
"description" => [Validators::userString(512)]
"name" => [Validators::lenBetween(0, 32), Validators::name()],
"description" => [Validators::lenBetween(0, 512)]
], function (HttpRequest $req) use ($response) {
$this->gateway->insert($req["name"], $req["description"]);
$description = htmlspecialchars($req["description"]);
$this->gateway->insert($req["name"], $description);
$results = ["results" => $this->gateway->listResults()];
return call_user_func_array($response, [$results]);
});
}

@ -0,0 +1,19 @@
<?php
namespace App\Validation;
class FunctionValidator extends Validator {
private $validate_fn;
/**
* @param callable $validate_fn the validate function. Must have the same signature as the {@link Validator::validate()} method.
*/
public function __construct(callable $validate_fn) {
$this->validate_fn = $validate_fn;
}
public function validate(string $name, $val): array {
return call_user_func_array($this->validate_fn, [$name, $val]);
}
}

@ -11,7 +11,7 @@ class ValidationFail implements \JsonSerializable {
* @param string $message
* @param string $kind
*/
protected function __construct(string $kind, string $message) {
public function __construct(string $kind, string $message) {
$this->message = $message;
$this->kind = $kind;
}
@ -31,4 +31,5 @@ class ValidationFail implements \JsonSerializable {
public static function notFound(string $message): ValidationFail {
return new ValidationFail("not found", $message);
}
}

@ -8,24 +8,47 @@ namespace App\Validation;
class Validators {
/**
* @return Validator a validator that validates non-empty strings
* @return Validator a validator that validates a given regex
*/
public static function nonEmpty(): Validator {
public static function regex(string $regex): Validator {
return new SimpleFunctionValidator(
fn($str) => !empty($str),
fn(string $name) => [FieldValidationFail::empty($name)]
fn(string $str) => preg_match($regex, $str),
fn(string $name) => [new FieldValidationFail($name, "field does not validates pattern $regex")]
);
}
public static function shorterThan(int $limit): Validator {
return new SimpleFunctionValidator(
fn(string $str) => strlen($str) <= $limit,
fn(string $name) => [new FieldValidationFail($name, "field is longer than $limit chars.")]
);
/**
* @return Validator a validator that validates strings that only contains numbers, letters, accents letters, `-` and `_`.
*/
public static function name(): Validator {
return self::regex("/^[0-9a-zA-Zà-üÀ-Ü_-]*$/");
}
public static function userString(int $maxLen): Validator {
return self::nonEmpty()->then(self::shorterThan($maxLen));
/**
* @return Validator a validator that validates strings that only contains numbers, letters, accents letters, `-`, `_` and spaces.
*/
public static function nameWithSpaces(): Validator {
return self::regex("/^[0-9a-zA-Zà-üÀ-Ü _-]*$/");
}
/**
* Validate string if its length is between given range
* @param int $min minimum accepted length, inclusive
* @param int $max maximum accepted length, exclusive
* @return Validator
*/
public static function lenBetween(int $min, int $max): Validator {
return new FunctionValidator(
function (string $fieldName, string $str) use ($min, $max) {
$len = strlen($str);
if ($len >= $max) {
return [new FieldValidationFail($fieldName, "field is longer than $max chars.")];
}
if ($len < $min) {
return [new FieldValidationFail($fieldName, "field is shorted than $min chars.")];
}
return [];
}
);
}
}
Loading…
Cancel
Save