diff --git a/app/connection.php b/app/connection.php new file mode 100644 index 0000000..80d3fc8 --- /dev/null +++ b/app/connection.php @@ -0,0 +1,29 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + public function executeQuery(string $query, array $parameters = []): bool + { + $this->stmt = parent::prepare($query); + foreach ($parameters as $name => $value) { + $this->stmt->bindValue($name, $value[0], $value[1]); + } + + return $this->stmt->execute(); + } + + public function getResults(): array + { + return $this->stmt->fetchAll(); + } +} diff --git a/app/database_con.php b/app/database_con.php new file mode 100644 index 0000000..fe11c14 --- /dev/null +++ b/app/database_con.php @@ -0,0 +1,28 @@ +dsn = "mysql:host=".getenv("SMDB_HOST").";dbname=".getenv("SMDB_DATABASE").";charset=UTF8"; + $this->login = getenv("SMDB_USER"); + $this->password = getenv("SMDB_PASSWORD"); + } + + function connect(): int|Connection { + try { + $connection = new Connection($this->dsn,$this->login,$this->password); + } catch (PDOException $e){ + throw new PDOException($e->getMessage(), $e->getCode(), $e); + } + return $connection; + } +} diff --git a/app/gateway/user_gateway.php b/app/gateway/user_gateway.php new file mode 100644 index 0000000..f11703f --- /dev/null +++ b/app/gateway/user_gateway.php @@ -0,0 +1,84 @@ +token = new Token; + try { + $this->con = (new DatabaseCon)->connect(); + } catch(PDOException $e) { + throw new PDOException($e->getMessage(), $e->getCode(), $e); + } + } + + public function createUser(string $mail, string $hash, string $username) { + $query = "INSERT INTO user VALUES(UUID(), :mail, :hash, :username, CURDATE());"; + $this->con->executeQuery($query, array( + ':mail' => array($mail, PDO::PARAM_STR), + ':hash' => array($hash, PDO::PARAM_STR), + ':username' => array($username, PDO::PARAM_STR) + )); + + $query = "SELECT id FROM user WHERE email=:mail;"; + $this->con->executeQuery($query, array( + ':mail' => array($mail, PDO::PARAM_STR) + )); + $results = $this->con->getResults(); + + return $this->token->getNewJsonToken($results[0]['id']); + } + + // Delete User: (1:OK, 2:Unauthorize, 3:No User) + public function deleteUser(string $uuid) : int { + $query = "DELETE FROM user WHERE id=:uuid;"; + $this->con->executeQuery($query, array( + ':uuid' => array($uuid, PDO::PARAM_STR) + )); + + return 0; + } + + // Login User (get token) + public function login(string $mail, string $hash) { + $query = "SELECT hash, id FROM user WHERE email=:mail;"; + $this->con->executeQuery($query, array( + ':mail' => array($mail, PDO::PARAM_STR) + )); + $results = $this->con->getResults(); + + if(empty($results)) { + // Not Found + return 404; + } + if($hash !== (string) $results[0]['hash']) { + // Unauthorized + return 401; + } + + return json_encode($this->token->getNewJsonToken($results[0]['id'])); + } + + public function updateMail(string $uuid, string $new_mail) { + $query = "UPDATE user SET email=:new_mail WHERE id=:uuid;"; + $this->con->executeQuery($query, array( + ':new_mail' => array($new_mail, PDO::PARAM_STR), + ':uuid' => array($uuid, PDO::PARAM_STR) + )); + } + + public function updateUsername(string $uuid, string $new_username) { + $query = "UPDATE user SET username=:new_username WHERE id=:uuid;"; + $this->con->executeQuery($query, array( + ':new_username' => array($new_username, PDO::PARAM_STR), + ':uuid' => array($uuid, PDO::PARAM_STR) + )); + } +} \ No newline at end of file diff --git a/app/routes.php b/app/routes.php index cc84b83..5495bce 100644 --- a/app/routes.php +++ b/app/routes.php @@ -1,10 +1,14 @@ post('/user', function (Request $req, Response $res) { - $res->getBody()->write('/user'); + $req_body = $req->getParsedBody(); + $res->getBody()->write(json_encode((new UserGateway)->createUser($req_body['mail'], $req_body['password'], $req_body['user']))); return $res; }); // Delete User $app->delete('/user', function (Request $req, Response $res) { $token = $req->getHeader('Authorization')[0]; - - $res->getBody()->write('/user/' . $token); - return $res; + $uuid = (new Token)->getUuidFromToken($token); + $code = (new UserGateway)->deleteUser($uuid); + + switch($code) { + case 0: + return $res->withStatus(200); + case -1: + return $res->withStatus(401); + case -2: + return $res->withStatus(404); + } + return $res->withStatus(500); }); // Get Token - $app->get('/user/{uuid}/{hash}/token', function (Request $req, Response $res, $args) { - $uuid = $args['uuid']; + $app->get('/user/login/{mail}/{hash}', function (Request $req, Response $res, $args) { + $mail = $args['mail']; $hash = $args['hash']; - $res->getBody()->write('/user/' . $uuid . '/' . $hash); + $value = (new UserGateway)->login($mail, $hash); + // If error statusCode else token + if($value instanceOf int) { + return $res->withStatus($value); + } + $res->getBody()->write($value); return $res; }); // Update Mail $app->put('/user/mail', function(Request $req, Response $res) { $token = $req->getHeader('Authorization')[0]; - $mail = $req->getParsedBody()['mail']; - - $res->getBody()->write('/user/mail mail:'.$mail.' Auth:'.$token); - return $res; + $new_mail = $req->getParsedBody()['mail']; + if(!(new Token)->verifyToken($token)) { + return $res->withStatus(401); + } + + $uuid = (new Token)->getUuidFromToken($token); + (new UserGateway)->updateMail($uuid, $new_mail); + return $res->withStatus(200); }); // Update Username $app->put('/user/username', function(Request $req, Response $res) { $token = $req->getHeader('Authorization')[0]; - $username = $req->getParsedBody()['username']; - - $res->getBody()->write('/user/username username:'.$username.' Auth:'.$token); - return $res; + $new_username = $req->getParsedBody()['username']; + if(!(new Token)->verifyToken($token)) { + return $res->withStatus(401); + } + + $uuid = (new Token)->getUuidFromToken($token); + (new UserGateway)->updateUsername($uuid, $new_username); + return $res->withStatus(200); }); #### FILES #### @@ -89,5 +116,4 @@ return function (App $app) { $res->getBody()->write('/user/files'.' Auth:'.$token); return $res; }); - }; \ No newline at end of file diff --git a/app/token.php b/app/token.php new file mode 100644 index 0000000..734d49c --- /dev/null +++ b/app/token.php @@ -0,0 +1,46 @@ +path_to_key, 'r'); + #$this->key = fread($file, filesize($this->path_to_key)); + #fclose($file); + } + + // Return json containing JWT with uuid and exp + function getNewJsonToken(string $uuid) :array { + $payload = [ + 'uuid' => $uuid, + 'exp' => strtotime("+2month", time()) + ]; + + return ["token" => JWT::encode($payload, $this->key, 'HS256')]; + } + + // Verify the JWT authenticity + function verifyToken(string $jwt) :bool { + try { + JWT::decode($jwt, new Key($this->key, 'HS256')); + } catch (Exception $e) { + return false; + } + return true; + } + + // Get uuid from JWT + // Missing error handling on bad JWT + function getUuidFromToken(string $jwt) :string { + $decoded = (array) JWT::decode($jwt, new Key($this->key, 'HS256')); + return $decoded['uuid']; + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 6b8de2a..8613b6d 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "require": { "php": "^7.4 || ^8.0", "ext-json": "*", + "firebase/php-jwt": "^6.9", "monolog/monolog": "^2.8", "php-di/php-di": "^6.4", "slim/psr7": "^1.5", diff --git a/composer.lock b/composer.lock index 69ca691..43593ec 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "62d60de4b802ec31b840b9197e947981", + "content-hash": "3fbd2a92a15c13f23e25e12b030b51e6", "packages": [ { "name": "fig/http-message-util", @@ -62,6 +62,69 @@ }, "time": "2020-11-24T22:02:12+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v6.9.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "f03270e63eaccf3019ef0f32849c497385774e11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/f03270e63eaccf3019ef0f32849c497385774e11", + "reference": "f03270e63eaccf3019ef0f32849c497385774e11", + "shasum": "" + }, + "require": { + "php": "^7.4||^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.5||^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^1.0||^2.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.9.0" + }, + "time": "2023-10-05T00:24:42+00:00" + }, { "name": "laravel/serializable-closure", "version": "v1.3.2",