fix suggestions
continuous-integration/drone/push Build is passing Details

pull/94/head
maxime.batista 1 year ago
parent a7f5a71532
commit bfb216bfaf

@ -5,8 +5,8 @@ object Account {
name
age
email
phoneNumber
passwordHash
phone_number
password_hash
profile_picture
}

@ -4,6 +4,12 @@ DROP TABLE IF EXISTS Tactic;
DROP TABLE IF EXISTS Team;
DROP TABLE IF EXISTS User;
DROP TABLE IF EXISTS Member;
CREATE TABLE Admins
(
id integer PRIMARY KEY REFERENCES Account
);
CREATE TABLE Account
(
id integer PRIMARY KEY AUTOINCREMENT,
@ -11,8 +17,7 @@ CREATE TABLE Account
username varchar NOT NULL,
token varchar UNIQUE NOT NULL,
hash varchar NOT NULL,
profile_picture varchar NOT NULL,
is_admin boolean DEFAULT false NOT NULL
profile_picture varchar NOT NULL
);
CREATE TABLE Tactic

@ -0,0 +1,44 @@
<?php
namespace IQBall\Api;
use IQBall\Core\Control;
use IQBall\Core\ControlSchemaErrorResponseFactory;
use IQBall\Core\Http\HttpRequest;
use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Http\JsonHttpResponse;
use IQBall\Core\Validation\Validator;
class APIControl {
private static function errorFactory(): ControlSchemaErrorResponseFactory {
return new class () implements ControlSchemaErrorResponseFactory {
public function apply(array $failures): HttpResponse {
return new JsonHttpResponse($failures);
}
};
}
/**
* Runs given callback, if the request's payload json validates the given schema.
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
* The callback must accept an HttpRequest, and return an HttpResponse object.
* @return HttpResponse
*/
public static function runChecked(array $schema, callable $run): HttpResponse {
return Control::runChecked($schema, $run, self::errorFactory());
}
/**
* Runs given callback, if the given request data array validates the given schema.
* @param array<string, mixed> $data the request's data array.
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
* The callback must accept an HttpRequest, and return an HttpResponse object.
* @return HttpResponse
*/
public static function runCheckedFrom(array $data, array $schema, callable $run): HttpResponse {
return Control::runCheckedFrom($data, $schema, $run, self::errorFactory());
}
}

@ -2,6 +2,7 @@
namespace IQBall\Api\Controller;
use IQBall\Api\APIControl;
use IQBall\App\Control;
use IQBall\Core\Data\Account;
use IQBall\Core\Gateway\AccountGateway;
@ -9,8 +10,8 @@ use IQBall\Core\Http\HttpCodes;
use IQBall\Core\Http\HttpRequest;
use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Http\JsonHttpResponse;
use IQBall\Core\Validation\DefaultValidators;
use IQBall\Core\Model\AuthModel;
use IQBall\Core\Validation\DefaultValidators;
use IQBall\Core\Validation\ValidationFail;
class APIAccountsController {
@ -18,6 +19,7 @@ class APIAccountsController {
private AuthModel $authModel;
/**
* @param AuthModel $model
* @param AccountGateway $accounts
*/
public function __construct(AuthModel $model, AccountGateway $accounts) {
@ -31,7 +33,7 @@ class APIAccountsController {
* @return HttpResponse
*/
public function listUsers(array $request): HttpResponse {
return Control::runCheckedFrom($request, [
return APIControl::runCheckedFrom($request, [
'start' => [DefaultValidators::isUnsignedInteger()],
'n' => [DefaultValidators::isUnsignedInteger()],
'search' => [DefaultValidators::lenBetween(0, 256)],
@ -42,7 +44,7 @@ class APIAccountsController {
"users" => $users,
"totalCount" => $this->accounts->totalCount(),
]);
}, true);
});
}
/**
@ -60,7 +62,7 @@ class APIAccountsController {
}
public function addUser(): HttpResponse {
return Control::runChecked([
return APIControl::runChecked([
"username" => [DefaultValidators::name()],
"email" => [DefaultValidators::email()],
"password" => [DefaultValidators::password()],
@ -76,20 +78,20 @@ class APIAccountsController {
return new JsonHttpResponse([
"id" => $account->getUser()->getId(),
]);
}, true);
});
}
public function removeUsers(): HttpResponse {
return Control::runChecked([
return APIControl::runChecked([
"identifiers" => [DefaultValidators::array(), DefaultValidators::forall(DefaultValidators::isUnsignedInteger())],
], function (HttpRequest $req) {
$this->accounts->removeAccounts($req["identifiers"]);
return HttpResponse::fromCode(HttpCodes::OK);
}, true);
});
}
public function updateUser(int $id): HttpResponse {
return Control::runChecked([
return APIControl::runChecked([
"email" => [DefaultValidators::email()],
"username" => [DefaultValidators::name()],
"isAdmin" => [DefaultValidators::bool()],
@ -101,6 +103,6 @@ class APIAccountsController {
$this->authModel->update($id, $req["email"], $req["username"], $req["isAdmin"]);
return HttpResponse::fromCode(HttpCodes::OK);
}, true);
});
}
}

@ -2,6 +2,7 @@
namespace IQBall\Api\Controller;
use IQBall\Api\APIControl;
use IQBall\App\Control;
use IQBall\Core\Http\HttpCodes;
use IQBall\Core\Http\HttpRequest;
@ -26,7 +27,7 @@ class APIAuthController {
* @return HttpResponse
*/
public function authorize(): HttpResponse {
return Control::runChecked([
return APIControl::runChecked([
"email" => [DefaultValidators::email(), DefaultValidators::lenBetween(5, 256)],
"password" => [DefaultValidators::password()],
], function (HttpRequest $req) {
@ -38,6 +39,6 @@ class APIAuthController {
}
return new JsonHttpResponse(["authorization" => $account->getToken()]);
}, true);
});
}
}

@ -2,7 +2,7 @@
namespace IQBall\Api\Controller;
use IQBall\App\Control;
use IQBall\Api\APIControl;
use IQBall\Core\Data\Account;
use IQBall\Core\Data\TacticInfo;
use IQBall\Core\Http\HttpCodes;
@ -32,7 +32,7 @@ class APITacticController {
* @return HttpResponse
*/
public function updateName(int $tactic_id, Account $account): HttpResponse {
return Control::runChecked([
return APIControl::runChecked([
"name" => [DefaultValidators::lenBetween(1, 50), DefaultValidators::nameWithSpaces()],
], function (HttpRequest $request) use ($tactic_id, $account) {
@ -44,23 +44,23 @@ class APITacticController {
}
return HttpResponse::fromCode(HttpCodes::OK);
}, true);
});
}
/**
* @param int $id
* @param Account $account
* @return HttpResponse
*/
public function saveContent(int $id, Account $account): HttpResponse {
return Control::runChecked([
return APIControl::runChecked([
"content" => [],
], function (HttpRequest $req) use ($id) {
//TODO verify that the account has the rights to update the tactic content
if ($fail = $this->model->updateContent($id, json_encode($req["content"]))) {
return new JsonHttpResponse([$fail], HttpCodes::BAD_REQUEST);
}
return HttpResponse::fromCode(HttpCodes::OK);
}, true);
});
}

@ -90,7 +90,6 @@ class App {
if ($action->getAuthType() == Action::AUTH_ADMIN && !$account->getUser()->isAdmin()) {
return new JsonHttpResponse([ValidationFail::unauthorized()], HttpCodes::UNAUTHORIZED);
}
}
return $action->run($params, $session);

@ -0,0 +1,44 @@
<?php
namespace IQBall\App;
use IQBall\Core\Control;
use IQBall\Core\ControlSchemaErrorResponseFactory;
use IQBall\Core\Http\HttpCodes;
use IQBall\Core\Http\HttpRequest;
use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Validation\Validator;
class AppControl {
private static function errorFactory(): ControlSchemaErrorResponseFactory {
return new class () implements ControlSchemaErrorResponseFactory {
public function apply(array $failures): HttpResponse {
return ViewHttpResponse::twig("error.html.twig", ['failures' => $failures], HttpCodes::BAD_REQUEST);
}
};
}
/**
* Runs given callback, if the request's payload json validates the given schema.
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
* The callback must accept an HttpRequest, and return an HttpResponse object.
* @return HttpResponse
*/
public static function runChecked(array $schema, callable $run): HttpResponse {
return Control::runChecked($schema, $run, self::errorFactory());
}
/**
* Runs given callback, if the given request data array validates the given schema.
* @param array<string, mixed> $data the request's data array.
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
* The callback must accept an HttpRequest, and return an HttpResponse object.
* @return HttpResponse
*/
public static function runCheckedFrom(array $data, array $schema, callable $run): HttpResponse {
return Control::runCheckedFrom($data, $schema, $run, self::errorFactory());
}
}

@ -5,6 +5,7 @@ namespace IQBall\App\Controller;
use IQBall\App\Session\MutableSessionHandle;
use IQBall\App\Session\SessionHandle;
use IQBall\App\ViewHttpResponse;
use IQBall\Core\Gateway\AccountGateway;
use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Model\TacticModel;
use IQBall\Core\Model\TeamModel;

@ -1,6 +1,6 @@
<?php
namespace IQBall\App;
namespace IQBall\Core;
use IQBall\Core\Http\HttpCodes;
use IQBall\Core\Http\HttpRequest;
@ -11,27 +11,22 @@ use IQBall\Core\Validation\Validator;
class Control {
/**
* Runs given callback, if the request's json validates the given schema.
* Runs given callback, if the request's payload json validates the given schema.
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
* The callback must accept an HttpRequest, and return an HttpResponse object.
* @param bool $errorInJson if set to true, the returned response will be a JsonHttpResponse if the schema isn't validated
* @param ControlSchemaErrorResponseFactory $errorFactory an error factory to use if the request does not validate the required schema
* @return HttpResponse
*/
public static function runChecked(array $schema, callable $run, bool $errorInJson = false): HttpResponse {
public static function runChecked(array $schema, callable $run, ControlSchemaErrorResponseFactory $errorFactory): HttpResponse {
$request_body = file_get_contents('php://input');
$payload_obj = json_decode($request_body);
if (!$payload_obj instanceof \stdClass) {
$fail = new ValidationFail("bad-payload", "request body is not a valid json object");
if ($errorInJson) {
return new JsonHttpResponse([$fail], HttpCodes::BAD_REQUEST);
}
return ViewHttpResponse::twig("error.html.twig", ["failures" => [$fail]], HttpCodes::BAD_REQUEST);
return $errorFactory->apply([$fail]);
}
$payload = get_object_vars($payload_obj);
return self::runCheckedFrom($payload, $schema, $run, $errorInJson);
return self::runCheckedFrom($payload, $schema, $run, $errorFactory);
}
/**
@ -40,18 +35,15 @@ class Control {
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
* The callback must accept an HttpRequest, and return an HttpResponse object.
* @param bool $errorInJson if set to true, the returned response will be a JsonHttpResponse if the schema isn't validated
* @param ControlSchemaErrorResponseFactory $errorFactory an error factory to use if the request does not validate the required schema
* @return HttpResponse
*/
public static function runCheckedFrom(array $data, array $schema, callable $run, bool $errorInJson = false): HttpResponse {
public static function runCheckedFrom(array $data, array $schema, callable $run, ControlSchemaErrorResponseFactory $errorFactory): HttpResponse {
$fails = [];
$request = HttpRequest::from($data, $fails, $schema);
if (!empty($fails)) {
if ($errorInJson) {
return new JsonHttpResponse($fails, HttpCodes::BAD_REQUEST);
}
return ViewHttpResponse::twig("error.html.twig", ['failures' => $fails], HttpCodes::BAD_REQUEST);
return $errorFactory->apply($fails);
}
return call_user_func_array($run, [$request]);

@ -0,0 +1,14 @@
<?php
namespace IQBall\Core;
use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Validation\ValidationFail;
interface ControlSchemaErrorResponseFactory {
/**
* @param ValidationFail[] $failures
* @return HttpResponse
*/
public function apply(array $failures): HttpResponse;
}

@ -29,13 +29,22 @@ class AccountGateway {
}
public function updateAccount(int $id, string $name, string $email, string $token, bool $isAdmin): void {
$this->con->exec("UPDATE Account SET username = :username, email = :email, token = :token, is_admin = :is_admin WHERE id = :id", [
$this->con->exec("UPDATE Account SET username = :username, email = :email, token = :token WHERE id = :id", [
':username' => [$name, PDO::PARAM_STR],
':email' => [$email, PDO::PARAM_STR],
':token' => [$token, PDO::PARAM_STR],
':id' => [$id, PDO::PARAM_INT],
':is_admin' => [$isAdmin, PDO::PARAM_BOOL],
]);
$this->setIsAdmin($id, $isAdmin);
}
public function isAdmin(int $id): bool {
$stmnt = $this->con->prepare("SELECT * FROM Admins WHERE id = :id");
$stmnt->bindValue(':id', $id, PDO::PARAM_INT);
$stmnt->execute();
$result = $stmnt->fetchAll(PDO::FETCH_ASSOC);
return !empty($result);
}
@ -46,8 +55,11 @@ class AccountGateway {
* @return bool true if the given user exists
*/
public function setIsAdmin(int $id, bool $isAdmin): bool {
$stmnt = $this->con->prepare("UPDATE Account SET is_admin = :is_admin WHERE id = :id");
$stmnt->bindValue(':is_admin', $isAdmin);
if ($isAdmin) {
$stmnt = $this->con->prepare("INSERT INTO Admins VALUES(:id)");
} else {
$stmnt = $this->con->prepare("DELETE FROM Admins WHERE id = :id");
}
$stmnt->bindValue(':id', $id);
$stmnt->execute();
@ -92,7 +104,7 @@ class AccountGateway {
return null;
}
return new Account($acc["token"], new User($email, $acc["username"], $acc["id"], $acc["profile_picture"], $acc['is_admin']));
return new Account($acc["token"], new User($email, $acc["username"], $acc["id"], $acc["profile_picture"], $this->isAdmin($acc["id"])));
}
/**
@ -123,7 +135,7 @@ class AccountGateway {
return null;
}
return new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profile_picture"], $acc["is_admin"]));
return new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profile_picture"], $this->isAdmin($acc["id"])));
}
/**
@ -142,7 +154,7 @@ class AccountGateway {
":search" => [$searchString ?? "", PDO::PARAM_STR],
]
);
return array_map(fn(array $acc) => new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profile_picture"], $acc["is_admin"])), $res);
return array_map(fn(array $acc) => new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profile_picture"], $this->isAdmin($acc["id"]))), $res);
}
/**

Loading…
Cancel
Save