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

@ -6,6 +6,7 @@ use App\Http\HttpCodes;
use App\Http\HttpRequest; use App\Http\HttpRequest;
use App\Http\HttpResponse; use App\Http\HttpResponse;
use App\Http\JsonHttpResponse; use App\Http\JsonHttpResponse;
use App\Validation\ValidationFail;
class Control { class Control {
@ -18,8 +19,11 @@ class Control {
*/ */
public static function runChecked(array $schema, callable $run): HttpResponse { public static function runChecked(array $schema, callable $run): HttpResponse {
$request_body = file_get_contents('php://input'); $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); return self::runCheckedFrom($payload, $schema, $run);
} }

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

@ -32,12 +32,12 @@ class SampleFormController {
private function submitForm(array $form, callable $response): HttpResponse { private function submitForm(array $form, callable $response): HttpResponse {
return Control::runCheckedFrom($form, [ return Control::runCheckedFrom($form, [
"name" => [Validators::userString(32)], "name" => [Validators::lenBetween(0, 32), Validators::name()],
"description" => [Validators::userString(512)] "description" => [Validators::lenBetween(0, 512)]
], function (HttpRequest $req) use ($response) { ], 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()]; $results = ["results" => $this->gateway->listResults()];
return call_user_func_array($response, [$results]); 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 $message
* @param string $kind * @param string $kind
*/ */
protected function __construct(string $kind, string $message) { public function __construct(string $kind, string $message) {
$this->message = $message; $this->message = $message;
$this->kind = $kind; $this->kind = $kind;
} }
@ -31,4 +31,5 @@ class ValidationFail implements \JsonSerializable {
public static function notFound(string $message): ValidationFail { public static function notFound(string $message): ValidationFail {
return new ValidationFail("not found", $message); return new ValidationFail("not found", $message);
} }
} }

@ -8,24 +8,47 @@ namespace App\Validation;
class Validators { 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( return new SimpleFunctionValidator(
fn($str) => !empty($str), fn(string $str) => preg_match($regex, $str),
fn(string $name) => [FieldValidationFail::empty($name)] fn(string $name) => [new FieldValidationFail($name, "field does not validates pattern $regex")]
); );
} }
public static function shorterThan(int $limit): Validator { /**
return new SimpleFunctionValidator( * @return Validator a validator that validates strings that only contains numbers, letters, accents letters, `-` and `_`.
fn(string $str) => strlen($str) <= $limit, */
fn(string $name) => [new FieldValidationFail($name, "field is longer than $limit chars.")] 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