add basic api routes to get info on server, users and tactics

pull/94/head
Override-6 2 years ago committed by maxime.batista
parent 3ace793578
commit 9cde3af510

@ -7,7 +7,7 @@ object Account {
email email
phoneNumber phoneNumber
passwordHash passwordHash
profilePicture profile_picture
} }
object TacticFolder { object TacticFolder {

@ -6,7 +6,9 @@ require "../../sql/database.php";
require "../../src/index-utils.php"; require "../../src/index-utils.php";
use IQBall\Api\API; use IQBall\Api\API;
use IQBall\Api\Controller\APIAccountsController;
use IQBall\Api\Controller\APIAuthController; use IQBall\Api\Controller\APIAuthController;
use IQBall\Api\Controller\APIServerController;
use IQBall\Api\Controller\APITacticController; use IQBall\Api\Controller\APITacticController;
use IQBall\App\Session\PhpSessionHandle; use IQBall\App\Session\PhpSessionHandle;
use IQBall\Core\Action; use IQBall\Core\Action;
@ -17,6 +19,8 @@ use IQBall\Core\Gateway\TacticInfoGateway;
use IQBall\Core\Model\AuthModel; use IQBall\Core\Model\AuthModel;
use IQBall\Core\Model\TacticModel; use IQBall\Core\Model\TacticModel;
$basePath = get_public_path(__DIR__);
function getTacticController(): APITacticController { function getTacticController(): APITacticController {
return new APITacticController(new TacticModel(new TacticInfoGateway(new Connection(get_database())))); return new APITacticController(new TacticModel(new TacticInfoGateway(new Connection(get_database()))));
} }
@ -25,14 +29,30 @@ function getAuthController(): APIAuthController {
return new APIAuthController(new AuthModel(new AccountGateway(new Connection(get_database())))); return new APIAuthController(new AuthModel(new AccountGateway(new Connection(get_database()))));
} }
function getAccountController(): APIAccountsController {
$con = new Connection(get_database());
return new APIAccountsController(new AccountGateway($con));
}
function getServerController(): APIServerController {
global $basePath;
return new APIServerController($basePath, get_database());
}
function getRoutes(): AltoRouter { function getRoutes(): AltoRouter {
$router = new AltoRouter(); $router = new AltoRouter();
$router->setBasePath(get_public_path(__DIR__)); global $basePath;
$router->setBasePath($basePath);
$router->map("POST", "/auth", Action::noAuth(fn() => getAuthController()->authorize())); $router->map("POST", "/auth", Action::noAuth(fn() => getAuthController()->authorize()));
$router->map("POST", "/tactic/[i:id]/edit/name", Action::auth(fn(int $id, Account $acc) => getTacticController()->updateName($id, $acc))); $router->map("POST", "/tactic/[i:id]/edit/name", Action::auth(fn(int $id, Account $acc) => getTacticController()->updateName($id, $acc)));
$router->map("POST", "/tactic/[i:id]/save", Action::auth(fn(int $id, Account $acc) => getTacticController()->saveContent($id, $acc))); $router->map("POST", "/tactic/[i:id]/save", Action::auth(fn(int $id, Account $acc) => getTacticController()->saveContent($id, $acc)));
$router->map("GET", "/admin/list-users", Action::admin(fn() => getAccountController()->listUsers($_GET)));
$router->map("GET", "/admin/user/[i:id]", Action::admin(fn(int $id) => getAccountController()->getUser($id)));
$router->map("GET", "/admin/user/[i:id]/space", Action::admin(fn(int $id) => getTacticController()->getUserTactics($id)));
$router->map("GET", "/admin/server-info", Action::admin(fn() => getServerController()->getServerInfo()));
return $router; return $router;
} }

@ -1,5 +1,9 @@
<?php <?php
use IQBall\Core\Connection;
use IQBall\Core\Gateway\AccountGateway;
use IQBall\Core\Model\AuthModel;
/** /**
* @return PDO The PDO instance of the configuration's database connexion. * @return PDO The PDO instance of the configuration's database connexion.
*/ */
@ -22,6 +26,20 @@ function get_database(): PDO {
} }
} }
init_database($pdo);
return $pdo; return $pdo;
} }
function init_database(PDO $pdo): void {
$accounts = new AccountGateway(new Connection($pdo));
$defaultAccounts = ["maxime", "mael", "yanis", "vivien"];
foreach ($defaultAccounts as $name) {
$email = "$name@mail.com";
$id = $accounts->insertAccount($name, $email, AuthModel::generateToken(), password_hash("123456", PASSWORD_DEFAULT));
$accounts->setIsAdmin($id, true);
}
}

@ -11,7 +11,8 @@ CREATE TABLE Account
username varchar NOT NULL, username varchar NOT NULL,
token varchar UNIQUE NOT NULL, token varchar UNIQUE NOT NULL,
hash varchar NOT NULL, hash varchar NOT NULL,
profilePicture varchar NOT NULL profile_picture varchar NOT NULL,
is_admin boolean DEFAULT false NOT NULL
); );
CREATE TABLE Tactic CREATE TABLE Tactic
@ -25,13 +26,6 @@ CREATE TABLE Tactic
FOREIGN KEY (owner) REFERENCES Account FOREIGN KEY (owner) REFERENCES Account
); );
CREATE TABLE FormEntries
(
name varchar NOT NULL,
description varchar NOT NULL
);
CREATE TABLE Team CREATE TABLE Team
( (
id integer PRIMARY KEY AUTOINCREMENT NOT NULL, id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
@ -41,11 +35,10 @@ CREATE TABLE Team
second_color varchar NOT NULL second_color varchar NOT NULL
); );
CREATE TABLE Member CREATE TABLE Member
( (
id_team integer NOT NULL, id_team integer NOT NULL,
id_user integer NOT NULL, id_user integer NOT NULL,
role text CHECK (role IN ('COACH', 'PLAYER')) NOT NULL, role text CHECK (role IN ('COACH', 'PLAYER')) NOT NULL,
FOREIGN KEY (id_team) REFERENCES Team (id), FOREIGN KEY (id_team) REFERENCES Team (id),
FOREIGN KEY (id_user) REFERENCES Account (id) FOREIGN KEY (id_user) REFERENCES Account (id)

@ -4,6 +4,8 @@ namespace IQBall\Api;
use Exception; use Exception;
use IQBall\Core\Action; use IQBall\Core\Action;
use IQBall\Core\Data\Account;
use IQBall\Core\Http\HttpCodes;
use IQBall\Core\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Http\JsonHttpResponse; use IQBall\Core\Http\JsonHttpResponse;
use IQBall\Core\Validation\ValidationFail; use IQBall\Core\Validation\ValidationFail;
@ -27,7 +29,7 @@ class API {
/** /**
* @param array<string, mixed>|false $match * @param array<string, mixed>|false $match
* @param callable $tryGetAuthorization function to return an authorisation object for the given action (if required) * @param callable(): Account $tryGetAuthorization function to return account authorisation for the given action (if required)
* @return HttpResponse * @return HttpResponse
* @throws Exception * @throws Exception
*/ */
@ -41,15 +43,19 @@ class API {
throw new Exception("routed action is not an AppAction object."); throw new Exception("routed action is not an AppAction object.");
} }
$auth = null; $account = null;
if ($action->isAuthRequired()) { if ($action->getAuthType() != Action::NO_AUTH) {
$auth = call_user_func($tryGetAuthorization); $account = call_user_func($tryGetAuthorization);
if ($auth == null) { if ($account == null) {
return new JsonHttpResponse([ValidationFail::unauthorized("Missing or invalid 'Authorization' header.")]); return new JsonHttpResponse([ValidationFail::unauthorized("Missing or invalid 'Authorization' header.")], HttpCodes::UNAUTHORIZED);
}
if ($action->getAuthType() == Action::AUTH_ADMIN && !$account->getUser()->isAdmin()) {
return new JsonHttpResponse([ValidationFail::unauthorized()], HttpCodes::UNAUTHORIZED);
} }
} }
return $action->run($match['params'], $auth); return $action->run($match['params'], $account);
} }
} }

@ -0,0 +1,55 @@
<?php
namespace IQBall\Api\Controller;
use IQBall\App\Control;
use IQBall\Core\Data\Account;
use IQBall\Core\Gateway\AccountGateway;
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\Validation\ValidationFail;
class APIAccountsController {
private AccountGateway $accounts;
/**
* @param AccountGateway $accounts
*/
public function __construct(AccountGateway $accounts) {
$this->accounts = $accounts;
}
/**
* @param array<string, mixed> $request
* @return HttpResponse
*/
public function listUsers(array $request): HttpResponse {
return Control::runCheckedFrom($request, [
'start' => [DefaultValidators::isUnsignedInteger()],
'n' => [DefaultValidators::isUnsignedInteger()],
], function (HttpRequest $req) {
$accounts = $this->accounts->listAccounts(intval($req['start']), intval($req['n']));
$response = array_map(fn(Account $acc) => $acc->getUser(), $accounts);
return new JsonHttpResponse($response);
}, true);
}
/**
* @param int $userId
* @return HttpResponse given user information.
*/
public function getUser(int $userId): HttpResponse {
$acc = $this->accounts->getAccount($userId);
if ($acc == null) {
return new JsonHttpResponse([ValidationFail::notFound("User not found")], HttpCodes::NOT_FOUND);
}
return new JsonHttpResponse($acc->getUser());
}
}

@ -38,7 +38,6 @@ class APIAuthController {
} }
return new JsonHttpResponse(["authorization" => $account->getToken()]); return new JsonHttpResponse(["authorization" => $account->getToken()]);
}); }, true);
} }
} }

@ -0,0 +1,45 @@
<?php
namespace IQBall\Api\Controller;
use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Http\JsonHttpResponse;
class APIServerController {
private string $basePath;
private \PDO $pdo;
/**
* @param string $basePath
* @param \PDO $pdo
*/
public function __construct(string $basePath, \PDO $pdo) {
$this->basePath = $basePath;
$this->pdo = $pdo;
}
private function countLines(string $table): int {
$stmnt = $this->pdo->prepare("SELECT count(*) FROM $table");
$stmnt->execute();
$res = $stmnt->fetch(\PDO::FETCH_BOTH);
return $res[0];
}
/**
* @return HttpResponse some (useless) information about the server
*/
public function getServerInfo(): HttpResponse {
return new JsonHttpResponse([
'base_path' => $this->basePath,
'date' => (int) gettimeofday(true) * 1000,
'database' => [
'accounts' => $this->countLines("Account") . " line(s)",
'tactics' => $this->countLines("Tactic") . " line(s)",
'teams' => $this->countLines("Team") . " line(s)",
],
]);
}
}

@ -4,12 +4,12 @@ namespace IQBall\Api\Controller;
use IQBall\App\Control; use IQBall\App\Control;
use IQBall\Core\Data\Account; use IQBall\Core\Data\Account;
use IQBall\Core\Data\TacticInfo;
use IQBall\Core\Http\HttpCodes; use IQBall\Core\Http\HttpCodes;
use IQBall\Core\Http\HttpRequest; use IQBall\Core\Http\HttpRequest;
use IQBall\Core\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Http\JsonHttpResponse; use IQBall\Core\Http\JsonHttpResponse;
use IQBall\Core\Model\TacticModel; use IQBall\Core\Model\TacticModel;
use IQBall\Core\Validation\FieldValidationFail;
use IQBall\Core\Validation\DefaultValidators; use IQBall\Core\Validation\DefaultValidators;
/** /**
@ -44,7 +44,7 @@ class APITacticController {
} }
return HttpResponse::fromCode(HttpCodes::OK); return HttpResponse::fromCode(HttpCodes::OK);
}); }, true);
} }
/** /**
@ -60,6 +60,26 @@ class APITacticController {
return new JsonHttpResponse([$fail], HttpCodes::BAD_REQUEST); return new JsonHttpResponse([$fail], HttpCodes::BAD_REQUEST);
} }
return HttpResponse::fromCode(HttpCodes::OK); return HttpResponse::fromCode(HttpCodes::OK);
}); }, true);
} }
/**
* @param int $userId
* @return HttpResponse given user information.
*/
public function getUserTactics(int $userId): HttpResponse {
$tactics = $this->model->listAllOf($userId);
$response = array_map(fn(TacticInfo $t) => [
'id' => $t->getId(),
'name' => $t->getName(),
'court' => $t->getCourtType(),
'creation_date' => $t->getCreationDate(),
], $tactics);
return new JsonHttpResponse($response);
}
} }

@ -4,8 +4,10 @@ namespace IQBall\App;
use IQBall\App\Session\MutableSessionHandle; use IQBall\App\Session\MutableSessionHandle;
use IQBall\Core\Action; use IQBall\Core\Action;
use IQBall\Core\Http\HttpCodes;
use IQBall\Core\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Http\JsonHttpResponse; use IQBall\Core\Http\JsonHttpResponse;
use IQBall\Core\Validation\ValidationFail;
use Twig\Environment; use Twig\Environment;
use Twig\Error\LoaderError; use Twig\Error\LoaderError;
use Twig\Error\RuntimeError; use Twig\Error\RuntimeError;
@ -77,13 +79,18 @@ class App {
* @return HttpResponse * @return HttpResponse
*/ */
public static function runAction(string $authRoute, Action $action, array $params, MutableSessionHandle $session): HttpResponse { public static function runAction(string $authRoute, Action $action, array $params, MutableSessionHandle $session): HttpResponse {
if ($action->isAuthRequired()) { if ($action->getAuthType() != Action::NO_AUTH) {
$account = $session->getAccount(); $account = $session->getAccount();
if ($account == null) { if ($account == null) {
// put in the session the initial url the user wanted to get // put in the session the initial url the user wanted to get
$session->setInitialTarget($_SERVER['REQUEST_URI']); $session->setInitialTarget($_SERVER['REQUEST_URI']);
return HttpResponse::redirect($authRoute); return HttpResponse::redirect($authRoute);
} }
if ($action->getAuthType() == Action::AUTH_ADMIN && !$account->getUser()->isAdmin()) {
return new JsonHttpResponse([ValidationFail::unauthorized()], HttpCodes::UNAUTHORIZED);
}
} }
return $action->run($params, $session); return $action->run($params, $session);

@ -5,6 +5,7 @@ namespace IQBall\App;
use IQBall\Core\Http\HttpCodes; use IQBall\Core\Http\HttpCodes;
use IQBall\Core\Http\HttpRequest; use IQBall\Core\Http\HttpRequest;
use IQBall\Core\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Http\JsonHttpResponse;
use IQBall\Core\Validation\ValidationFail; use IQBall\Core\Validation\ValidationFail;
use IQBall\Core\Validation\Validator; use IQBall\Core\Validation\Validator;
@ -13,18 +14,24 @@ class Control {
* Runs given callback, if the request's json validates the given schema. * Runs given callback, if the request's json validates the given schema.
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object 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. * @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. * 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
* @return HttpResponse * @return HttpResponse
*/ */
public static function runChecked(array $schema, callable $run): HttpResponse { public static function runChecked(array $schema, callable $run, bool $errorInJson = false): HttpResponse {
$request_body = file_get_contents('php://input'); $request_body = file_get_contents('php://input');
$payload_obj = json_decode($request_body); $payload_obj = json_decode($request_body);
if (!$payload_obj instanceof \stdClass) { if (!$payload_obj instanceof \stdClass) {
$fail = new ValidationFail("bad-payload", "request body is not a valid json object"); $fail = new ValidationFail("bad-payload", "request body is not a valid json object");
if ($errorInJson) {
return new JsonHttpResponse([$fail]);
}
return ViewHttpResponse::twig("error.html.twig", ["failures" => [$fail]], HttpCodes::BAD_REQUEST); return ViewHttpResponse::twig("error.html.twig", ["failures" => [$fail]], HttpCodes::BAD_REQUEST);
} }
$payload = get_object_vars($payload_obj); $payload = get_object_vars($payload_obj);
return self::runCheckedFrom($payload, $schema, $run); return self::runCheckedFrom($payload, $schema, $run, $errorInJson);
} }
/** /**
@ -32,14 +39,18 @@ class Control {
* @param array<string, mixed> $data the request's data array. * @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 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. * @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. * 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
* @return HttpResponse * @return HttpResponse
*/ */
public static function runCheckedFrom(array $data, array $schema, callable $run): HttpResponse { public static function runCheckedFrom(array $data, array $schema, callable $run, bool $errorInJson = false): HttpResponse {
$fails = []; $fails = [];
$request = HttpRequest::from($data, $fails, $schema); $request = HttpRequest::from($data, $fails, $schema);
if (!empty($fails)) { if (!empty($fails)) {
if ($errorInJson) {
return new JsonHttpResponse($fails);
}
return ViewHttpResponse::twig("error.html.twig", ['failures' => $fails], HttpCodes::BAD_REQUEST); return ViewHttpResponse::twig("error.html.twig", ['failures' => $fails], HttpCodes::BAD_REQUEST);
} }

@ -9,23 +9,27 @@ use IQBall\Core\Http\HttpResponse;
* @template S session * @template S session
*/ */
class Action { class Action {
public const NO_AUTH = 1;
public const AUTH_USER = 2;
public const AUTH_ADMIN = 3;
/** /**
* @var callable(mixed[], S): HttpResponse $action action to call * @var callable(mixed[], S): HttpResponse $action action to call
*/ */
protected $action; protected $action;
private bool $isAuthRequired; private int $authType;
/** /**
* @param callable(mixed[], S): HttpResponse $action * @param callable(mixed[], S): HttpResponse $action
*/ */
protected function __construct(callable $action, bool $isAuthRequired) { protected function __construct(callable $action, int $authType) {
$this->action = $action; $this->action = $action;
$this->isAuthRequired = $isAuthRequired; $this->authType = $authType;
} }
public function isAuthRequired(): bool { public function getAuthType(): int {
return $this->isAuthRequired; return $this->authType;
} }
/** /**
@ -45,7 +49,7 @@ class Action {
* @return Action<S> an action that does not require to have an authorization. * @return Action<S> an action that does not require to have an authorization.
*/ */
public static function noAuth(callable $action): Action { public static function noAuth(callable $action): Action {
return new Action($action, false); return new Action($action, self::NO_AUTH);
} }
/** /**
@ -53,6 +57,14 @@ class Action {
* @return Action<S> an action that does require to have an authorization. * @return Action<S> an action that does require to have an authorization.
*/ */
public static function auth(callable $action): Action { public static function auth(callable $action): Action {
return new Action($action, true); return new Action($action, self::AUTH_USER);
}
/**
* @param callable(mixed[], S): HttpResponse $action
* @return Action<S> an action that does require to have an authorization, and to be an administrator.
*/
public static function admin(callable $action): Action {
return new Action($action, self::AUTH_ADMIN);
} }
} }

@ -2,8 +2,6 @@
namespace IQBall\Core\Data; namespace IQBall\Core\Data;
use _PHPStan_4c4f22f13\Nette\Utils\Json;
class User implements \JsonSerializable { class User implements \JsonSerializable {
/** /**
* @var string $email user's mail address * @var string $email user's mail address
@ -25,17 +23,31 @@ class User implements \JsonSerializable {
*/ */
private string $profilePicture; private string $profilePicture;
/**
* @var bool isAdmin
*/
private bool $isAdmin;
/** /**
* @param string $email * @param string $email
* @param string $name * @param string $name
* @param int $id * @param int $id
* @param string $profilePicture * @param string $profilePicture
* @param bool $isAdmin
*/ */
public function __construct(string $email, string $name, int $id, string $profilePicture) { public function __construct(string $email, string $name, int $id, string $profilePicture, bool $isAdmin) {
$this->email = $email; $this->email = $email;
$this->name = $name; $this->name = $name;
$this->id = $id; $this->id = $id;
$this->profilePicture = $profilePicture; $this->profilePicture = $profilePicture;
$this->isAdmin = $isAdmin;
}
/**
* @return bool
*/
public function isAdmin(): bool {
return $this->isAdmin;
} }
/** /**

@ -2,6 +2,7 @@
namespace IQBall\Core\Gateway; namespace IQBall\Core\Gateway;
use Cassandra\PreparedStatement;
use IQBall\Core\Connection; use IQBall\Core\Connection;
use IQBall\Core\Data\Account; use IQBall\Core\Data\Account;
use IQBall\Core\Data\User; use IQBall\Core\Data\User;
@ -18,7 +19,7 @@ class AccountGateway {
} }
public function insertAccount(string $name, string $email, string $token, string $hash, string $profilePicture): int { public function insertAccount(string $name, string $email, string $token, string $hash, string $profilePicture): int {
$this->con->exec("INSERT INTO Account(username, hash, email, token,profilePicture) VALUES (:username,:hash,:email,:token,:profilePic)", [ $this->con->exec("INSERT INTO Account(username, hash, email, token,profile_picture) VALUES (:username,:hash,:email,:token,:profilePic)", [
':username' => [$name, PDO::PARAM_STR], ':username' => [$name, PDO::PARAM_STR],
':hash' => [$hash, PDO::PARAM_STR], ':hash' => [$hash, PDO::PARAM_STR],
':email' => [$email, PDO::PARAM_STR], ':email' => [$email, PDO::PARAM_STR],
@ -28,6 +29,22 @@ class AccountGateway {
return intval($this->con->lastInsertId()); return intval($this->con->lastInsertId());
} }
/**
* promote or demote a user to server administrator
* @param int $id
* @param bool $isAdmin true to promote, false to demote
* @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);
$stmnt->bindValue(':id', $id);
$stmnt->execute();
return $stmnt->rowCount() > 0;
}
/** /**
* @param string $email * @param string $email
* @return array<string, mixed>|null * @return array<string, mixed>|null
@ -66,7 +83,7 @@ class AccountGateway {
return null; return null;
} }
return new Account($acc["token"], new User($email, $acc["username"], $acc["id"], $acc["profilePicture"])); return new Account($acc["token"], new User($email, $acc["username"], $acc["id"], $acc["profile_picture"], $acc['is_admin']));
} }
/** /**
@ -74,13 +91,48 @@ class AccountGateway {
* @return Account|null * @return Account|null
*/ */
public function getAccountFromToken(string $token): ?Account { public function getAccountFromToken(string $token): ?Account {
$acc = $this->con->fetch("SELECT * FROM Account WHERE token = :token", [':token' => [$token, PDO::PARAM_STR]])[0] ?? null; $stmnt = $this->con->prepare("SELECT * FROM Account WHERE token = :token");
if (empty($acc)) { $stmnt->bindValue(':token', $token);
return $this->getAccountFrom($stmnt);
}
/**
* @param int $id get an account from given identifier
* @return Account|null
*/
public function getAccount(int $id): ?Account {
$stmnt = $this->con->prepare("SELECT * FROM Account WHERE id = :id");
$stmnt->bindValue(':id', $id);
return $this->getAccountFrom($stmnt);
}
private function getAccountFrom(\PDOStatement $stmnt): ?Account {
$stmnt->execute();
$acc = $stmnt->fetch(PDO::FETCH_ASSOC);
if ($acc == null) {
return null; return null;
} }
return new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profilePicture"])); return new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profile_picture"], $acc["is_admin"]));
} }
/**
* Return a list containing n accounts from a given starting index
*
* @param integer $n the number of accounts to retrieve
* @param int $start starting index of the list content
* @return Account[]
*/
public function listAccounts(int $start, int $n): ?array {
$res = $this->con->fetch(
"SELECT * FROM Account ORDER BY email LIMIT :offset, :n",
[
":offset" => [$start, PDO::PARAM_INT],
":n" => [$n, PDO::PARAM_INT],
]
);
return array_map(fn(array $acc) => new Account($acc["email"], new User($acc["username"], $acc["token"], $acc["id"], $acc["profile_picture"], $acc["is_admin"])), $res);
}
} }

@ -41,12 +41,12 @@ class MemberGateway {
*/ */
public function getMembersOfTeam(int $teamId): array { public function getMembersOfTeam(int $teamId): array {
$rows = $this->con->fetch( $rows = $this->con->fetch(
"SELECT a.id,a.email,a.username,a.profilePicture,m.role FROM Account a,team t,Member m WHERE t.id = :id AND m.id_team = t.id AND m.id_user = a.id", "SELECT a.id,a.email,a.username,a.profile_picture,m.role FROM Account a,team t,Member m WHERE t.id = :id AND m.id_team = t.id AND m.id_user = a.id",
[ [
":id" => [$teamId, PDO::PARAM_INT], ":id" => [$teamId, PDO::PARAM_INT],
] ]
); );
return array_map(fn($row) => new Member(new User($row['email'], $row['username'], $row['id'], $row['profilePicture']), $teamId, $row['role']), $rows); return array_map(fn($row) => new Member(new User($row['email'], $row['username'], $row['id'], $row['profile_picture'], $row['is_admin']), $teamId, $row['role']), $rows);
} }
/** /**

@ -83,6 +83,24 @@ class TacticInfoGateway {
return $res; return $res;
} }
/**
* Return a list containing the nth last tactics of a given user id
*
* @param integer $user_id
* @return TacticInfo[]
*/
public function listAllOf(int $user_id): array {
$res = $this->con->fetch(
"SELECT * FROM Tactic WHERE owner = :owner_id ORDER BY creation_date DESC",
[
":owner_id" => [$user_id, PDO::PARAM_STR],
]
);
return array_map(fn(array $t) => new TacticInfo($t['id'], $t["name"], strtotime($t["creation_date"]), $t["owner"], CourtType::fromName($t['court_type']), $t['content']), $res);
}
/** /**
* @param string $name * @param string $name
* @param int $owner * @param int $owner

@ -29,7 +29,13 @@ class AuthModel {
* @return Account|null the registered account or null if failures occurred * @return Account|null the registered account or null if failures occurred
* @throws Exception * @throws Exception
*/ */
public function register(string $username, string $password, string $confirmPassword, string $email, array &$failures): ?Account { public function register(
string $username,
string $password,
string $confirmPassword,
string $email,
array &$failures
): ?Account {
if ($password != $confirmPassword) { if ($password != $confirmPassword) {
$failures[] = new FieldValidationFail("confirmpassword", "Le mot de passe et la confirmation ne sont pas les mêmes."); $failures[] = new FieldValidationFail("confirmpassword", "Le mot de passe et la confirmation ne sont pas les mêmes.");
@ -47,7 +53,7 @@ class AuthModel {
$token = $this->generateToken(); $token = $this->generateToken();
$accountId = $this->gateway->insertAccount($username, $email, $token, $hash, self::DEFAULT_PROFILE_PICTURE); $accountId = $this->gateway->insertAccount($username, $email, $token, $hash, self::DEFAULT_PROFILE_PICTURE);
return new Account($token, new User($email, $username, $accountId, self::DEFAULT_PROFILE_PICTURE)); return new Account($token, new User($email, $username, $accountId, self::DEFAULT_PROFILE_PICTURE, false));
} }
/** /**
@ -55,7 +61,7 @@ class AuthModel {
* @return string * @return string
* @throws Exception * @throws Exception
*/ */
private function generateToken(): string { public static function generateToken(): string {
return base64_encode(random_bytes(64)); return base64_encode(random_bytes(64));
} }

@ -62,6 +62,18 @@ class TacticModel {
return $this->tactics->getLast($nb, $ownerId); return $this->tactics->getLast($nb, $ownerId);
} }
/**
* Return a list containing all the tactics of a given user
* NOTE: if given user id does not match any user, this function returns an empty array
*
* @param integer $user_id
* @return TacticInfo[] | null
*/
public function listAllOf(int $user_id): ?array {
return$this->tactics->listAllOf($user_id);
}
/** /**
* Get all the tactics of the owner * Get all the tactics of the owner
* *

@ -68,7 +68,11 @@ class DefaultValidators {
public static function isInteger(): Validator { public static function isInteger(): Validator {
return self::regex("/^[0-9]+$/"); return self::regex("/^-[0-9]+$/", "field is not an integer");
}
public static function isUnsignedInteger(): Validator {
return self::regex("/^[0-9]+$/", "field is not an unsigned integer");
} }
public static function isIntInRange(int $min, int $max): Validator { public static function isIntInRange(int $min, int $max): Validator {

Loading…
Cancel
Save