diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..771443b --- /dev/null +++ b/.drone.yml @@ -0,0 +1,71 @@ +kind: pipeline +type: docker +name: smartfit + +trigger: + event: + - push + +steps: + - name: build_api_image + image: plugins/docker + settings: + dockerfile: Dockerfile + context: . + registry: hub.codefirst.iut.uca.fr + repo: hub.codefirst.iut.uca.fr/remi.arnal/smartfit_api + username: + from_secret: cf_username + password: + from_secret: cf_password + + - name: sonarqube + image: sonarsource/sonar-scanner-cli + environment: + SONAR_TOKEN: + from_secret: sonar_token + commands: + - sonar-scanner -Dsonar.projectKey=SmartFit_API + -Dsonar.sources=./app/ + -Dsonar.inclusions=**/*.php + -Dsonar.login=$${SONAR_TOKEN} + -Dsonar.language=php + -Dsonar.host.url=https://codefirst.iut.uca.fr/sonar + depends_on: [build_api_image] + + - name: deploy_api_image + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + environment: + IMAGENAME: hub.codefirst.iut.uca.fr/remi.arnal/smartfit_api:latest + CONTAINERNAME: smartfit_api + COMMAND: create + OVERWRITE: true + CODEFIRST_CLIENTDRONE_ENV_SMDB_HOST: SmartFit-smartfit_db + CODEFIRST_CLIENTDRONE_ENV_SMDB_DATABASE: + from_secret: db_database + CODEFIRST_CLIENTDRONE_ENV_SMDB_USER: + from_secret: db_user + CODEFIRST_CLIENTDRONE_ENV_SMDB_PASSWORD: + from_secret: db_password + CODEFIRST_CLIENTDRONE_ENV_ROOT_PASSWORD: + from_secret: db_root_password + ADMINS: remiarnal,enzojolys,othmanebenjelloun + depends_on: [ build_api_image ] + + - name: deploy_database_image + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + environment: + IMAGENAME: mariadb:11.1.2 + CONTAINERNAME: smartfit_db + PRIVATE : true + COMMAND: create + CODEFIRST_CLIENTDRONE_ENV_MARIADB_ROOT_PASSWORD: + from_secret: db_root_password + CODEFIRST_CLIENTDRONE_ENV_MARIADB_DATABASE: + from_secret: db_database + CODEFIRST_CLIENTDRONE_ENV_MARIADB_USER: + from_secret: db_user + CODEFIRST_CLIENTDRONE_ENV_MARIADB_PASSWORD: + from_secret: db_password + ADMINS: remiarnal,enzojolys,othmanebenjelloun + depends_on: [ deploy_api_image ] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a08d4ed --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM php:8.1-apache +RUN apt-get update && apt-get install -y git zip +RUN docker-php-ext-install pdo pdo_mysql +COPY . /var/www/html/ +WORKDIR /var/www/html/ +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer +RUN composer update && composer install +RUN echo "file_uploads = On\nmemory_limit = 64M\nupload_max_filesize = 64M\npost_max_size = 64M\nmax_execution_time = 600\n" > /usr/local/etc/php/conf.d/uploads.ini +RUN a2enmod rewrite +RUN a2enmod actions +RUN service apache2 restart +RUN mkdir -p /home/hel/smartfit_hdd +RUN chmod -R 777 /home/hel/smartfit_hdd \ No newline at end of file diff --git a/SMDB_ENV.sh b/SMDB_ENV.sh new file mode 100755 index 0000000..ab71d9f --- /dev/null +++ b/SMDB_ENV.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +export SMDB_HOST=localhost +export SMDB_DATABASE=smartfit +export SMDB_USER=manager +export SMDB_PASSWORD=manager diff --git a/app/database_con.php b/app/database_con.php index fe11c14..d609295 100644 --- a/app/database_con.php +++ b/app/database_con.php @@ -8,7 +8,7 @@ class DatabaseCon{ private string $login; private string $password; - function __construct(){ + public function __construct(){ if (getenv("SMDB_HOST") == null || getenv("SMDB_DATABASE") == null || getenv("SMDB_USER") == null || getenv("SMDB_PASSWORD") == null){ throw new PDOException("ENV variables not found"); } @@ -17,7 +17,7 @@ class DatabaseCon{ $this->password = getenv("SMDB_PASSWORD"); } - function connect(): int|Connection { + public function connect(): int|Connection { try { $connection = new Connection($this->dsn,$this->login,$this->password); } catch (PDOException $e){ diff --git a/app/database_init.php b/app/database_init.php new file mode 100644 index 0000000..eeb6c36 --- /dev/null +++ b/app/database_init.php @@ -0,0 +1,44 @@ +con = (new DatabaseCon)->connect(); + #} catch(PDOException $e) { + # throw new PDOException($e->getMessage(), $e->getCode(), $e); + $this->createUserTable(); + $this->createFileTable(); + putenv("IS_DB_INIT=true"); + } + } + + private function createUserTable() { + $query = 'CREATE TABLE IF NOT EXISTS user ( + id UUID PRIMARY KEY, + email VARCHAR(100) UNIQUE, + hash VARCHAR(255), + username VARCHAR(20) DEFAULT \'Change Me!\', + creation_date DATE);'; + + $this->con->executeQuery($query); + } + + private function createFileTable() { + $query = 'CREATE TABLE IF NOT EXISTS file ( + id UUID PRIMARY KEY, + user_id UUID REFERENCES `user`(`id`) ON DELETE CASCADE, + filename VARCHAR(100) DEFAULT CURDATE(), + category VARCHAR(50), + creation_date DATETIME, + import_date DATE);'; + + $this->con->executeQuery($query); + } +} diff --git a/app/gateway/file_gateway.php b/app/gateway/file_gateway.php index 8b8db99..c4d249f 100644 --- a/app/gateway/file_gateway.php +++ b/app/gateway/file_gateway.php @@ -16,12 +16,14 @@ class FileGateway { } } - public function createFile(string $filename, string $user_uuid) { - $query = "INSERT INTO file VALUES(UUID(), :user_uuid, :filename, CURDATE());"; + public function createFile(string $filename, string $user_uuid, string $category, string $creation_date) { + $query = "INSERT INTO file VALUES(UUID(), :user_uuid, :filename, :category, :creation_date ,CURDATE());"; try { $this->con->executeQuery($query, array( + ':user_uuid' => array($user_uuid, PDO::PARAM_STR), ':filename' => array($filename, PDO::PARAM_STR), - ':user_uuid' => array($user_uuid, PDO::PARAM_STR) + ':category' => array($category, PDO::PARAM_STR), + ':creation_date' => array($creation_date, PDO::PARAM_STR) )); } catch (PDOException $e) { return -1; @@ -61,7 +63,7 @@ class FileGateway { } public function listFiles(string $user_uuid) { - $query = "SELECT f.id, f.filename FROM file f, user u WHERE f.user_id=u.id and u.id=:user_uuid;"; + $query = "SELECT f.id, f.filename, f.category, f.creation_date FROM file f, user u WHERE f.user_id=u.id and u.id=:user_uuid;"; try { $this->con->executeQuery($query, array( ':user_uuid' => array($user_uuid, PDO::PARAM_STR) @@ -76,9 +78,11 @@ class FileGateway { $rows[] = [ 'uuid' => $row['id'], 'filename' => $row['filename'], + 'category' => $row['category'], + 'creation_date' => $row['creation_date'] ]; } return $rows; } -} \ No newline at end of file +} diff --git a/app/gateway/user_gateway.php b/app/gateway/user_gateway.php index 807e8e5..1f75dbd 100644 --- a/app/gateway/user_gateway.php +++ b/app/gateway/user_gateway.php @@ -6,8 +6,6 @@ use PDOException; use PDO; use Config\Token; -use function PHPUnit\Framework\isEmpty; - class UserGateway { private Connection $con; private Token $token; @@ -39,14 +37,16 @@ class UserGateway { // Delete User: (1:OK, 2:Unauthorize, 3:No User) public function deleteUser(string $uuid) : int { - $query = "DELETE FROM user WHERE id=:uuid;"; + $query = "DELETE FROM user WHERE id=:uuid RETURNING row_count();"; try { $this->con->executeQuery($query, array( ':uuid' => array($uuid, PDO::PARAM_STR) )); + $results = $this->con->getResults(); } catch (PDOException $e) { - return -1; + return -2; } + if(count($results) === 0) return -1; return 0; } @@ -69,6 +69,21 @@ class UserGateway { return json_encode($this->token->getNewJsonToken($results[0]['id'])); } + public function getInfo(string $uuid) { + $query = "SELECT email, username FROM user WHERE id=:uuid;"; + try { + $this->con->executeQuery($query,array( + ':uuid' => array($uuid, PDO::PARAM_STR) + )); + $results = $this->con->getResults(); + } catch(PDOException $e) { + return -2; + } + if(count($results) === 0) return -1; + + return ["email" => $results[0]['email'], "username" => $results[0]['username']]; + } + public function updateMail(string $uuid, string $new_email) { $query = "UPDATE user SET email=:new_email WHERE id=:uuid;"; try { @@ -96,4 +111,4 @@ class UserGateway { return 0; } -} \ No newline at end of file +} diff --git a/app/routes.php b/app/routes.php index d43f3f0..d5c4a4b 100644 --- a/app/routes.php +++ b/app/routes.php @@ -1,19 +1,36 @@ options('/{routes:.+}', function ($request, $response, $args) { + return $response; + }); + $app->add(function ($request, $handler) { + $response = $handler->handle($request); + return $response + ->withHeader('Access-Control-Allow-Origin', '*') + ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization') + ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'); + }); + $app->get('/', function (Request $req, Response $res) { $res->getBody()->write('SmartFit-API is working!'); return $res; @@ -48,6 +65,8 @@ return function (App $app) { return $res->withStatus(200); case -1: return $res->withStatus(404); + case -2: + return $res->withStatus(500); } return $res->withStatus(500); }); @@ -71,6 +90,25 @@ return function (App $app) { return $res; }); + $app->get('/user/info', function(Request $req, Response $res) { + $token = $req->getHeader('Authorization')[0]; + if(!(new Token)->verifyToken($token)) { + return $res->withStatus(401); + } + + $uuid = (new Token)->getUuidFromToken($token); + $code = (new UserGateway)->getInfo($uuid); + switch($code) { + case -1: + return $res->withStatus(404); + case -2: + return $res->withStatus(500); + } + + $res->getBody()->write(json_encode($code)); + return $res; + }); + // Update Mail $app->put('/user/email', function(Request $req, Response $res) { $token = $req->getHeader('Authorization')[0]; @@ -187,9 +225,12 @@ return function (App $app) { $uuid = (new Token)->getUuidFromToken($token); $file = $req->getUploadedFiles()['file']; + $category = $req->getParsedBody()['SmartFit_Category']; + $creation_date = $req->getParsedBody()['SmartFit_Date']; $filename = $file->getClientFilename(); + $code = (new FileGateway)->listFiles($uuid); - if(in_array($filename, $code, false)) return $res->withStatus(409); + if(array_search($filename, array_column($code, 'filename'), false) !== false) return $res->withStatus(409); $file_save_folder = $save_folder.'/'.$uuid.'/'; if(!is_dir($file_save_folder)) { @@ -197,8 +238,13 @@ return function (App $app) { } $file->moveTo($file_save_folder.'/'.$filename); - $code = (new FileGateway)->createFile($filename, $uuid); + $code = (new FileGateway)->createFile($filename, $uuid, $category, $creation_date); if($code === -1) return $res->withStatus(500); + return $res->withStatus(200); }); -}; \ No newline at end of file + + $app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function ($request, $response) { + throw new HttpNotFoundException($request); + }); +}; diff --git a/app/token.php b/app/token.php index 734d49c..0123ae8 100644 --- a/app/token.php +++ b/app/token.php @@ -10,7 +10,7 @@ class Token { // Need to be in a config file private string $path_to_key="../sym_keyfile.key"; - function __construct() + public function __construct() { #$file = fopen($this->path_to_key, 'r'); #$this->key = fread($file, filesize($this->path_to_key)); @@ -18,7 +18,7 @@ class Token { } // Return json containing JWT with uuid and exp - function getNewJsonToken(string $uuid) :array { + public function getNewJsonToken(string $uuid) :array { $payload = [ 'uuid' => $uuid, 'exp' => strtotime("+2month", time()) @@ -28,7 +28,7 @@ class Token { } // Verify the JWT authenticity - function verifyToken(string $jwt) :bool { + public function verifyToken(string $jwt) :bool { try { JWT::decode($jwt, new Key($this->key, 'HS256')); } catch (Exception $e) { @@ -39,7 +39,7 @@ class Token { // Get uuid from JWT // Missing error handling on bad JWT - function getUuidFromToken(string $jwt) :string { + public function getUuidFromToken(string $jwt) :string { $decoded = (array) JWT::decode($jwt, new Key($this->key, 'HS256')); return $decoded['uuid']; } diff --git a/init.sql b/init.sql new file mode 100644 index 0000000..ab4fcd5 --- /dev/null +++ b/init.sql @@ -0,0 +1,35 @@ +CREATE TABLE user ( + `id` UUID PRIMARY KEY, + email VARCHAR(100) UNIQUE, + hash VARCHAR(255), + username VARCHAR(20) DEFAULT 'Change Me!', + creation_date DATE +); + +CREATE TABLE file ( + id UUID PRIMARY KEY, + `user_id` UUID REFERENCES `user`(`id`) ON DELETE CASCADE, + filename VARCHAR(100) DEFAULT CURDATE(), + import_date DATE, +); + +-- CREATE USER +INSERT INTO user VALUES (UUID(), MAIL, HASH, USERNAME, CURDATE()); + +-- DELETE USER +DELETE FROM user WHERE id=USER_ID; + +-- GET FILE LIST +SELECT id, import_date, title FROM file WHERE user_id=USER_ID; + +-- UPLOAD FILE +INSERT INTO file VALUES (UUID(), USER_ID, TITLE, CURDATE(), DATA); + +-- DELETE FILE +DELETE FROM file WHERE id=ID and USER_ID=USER_ID; + +-- UPDATE MAIL +UPDATE user SET mail=MAIL WHERE id=ID; + +-- UPDATE USERNAME +UPDATE user SET username=USERNAME WHERE id=ID; \ No newline at end of file diff --git a/public/index.php b/public/index.php index bc583c8..e385a8a 100644 --- a/public/index.php +++ b/public/index.php @@ -9,6 +9,9 @@ use App\Application\Settings\SettingsInterface; use DI\ContainerBuilder; use Slim\Factory\AppFactory; use Slim\Factory\ServerRequestCreatorFactory; +use Config\DatabaseInit; +require '../app/connection.php'; +require '../app/database_init.php'; require __DIR__ . '/../vendor/autoload.php'; @@ -76,6 +79,9 @@ $app->addBodyParsingMiddleware(); $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, $logError, $logErrorDetails); $errorMiddleware->setDefaultErrorHandler($errorHandler); +// Create DB +(new DatabaseInit); + // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); diff --git a/sym_keyfile.key b/sym_keyfile.key new file mode 100644 index 0000000..b5163f4 --- /dev/null +++ b/sym_keyfile.key @@ -0,0 +1 @@ +k,'wȏUv:uvT"z^&