From 6fe722fffb44ff83ed7480a0c83659ca32981cfc Mon Sep 17 00:00:00 2001 From: Kleber C Batista Date: Wed, 8 Feb 2017 23:14:55 -0200 Subject: [PATCH 1/5] Commit inicial com a base do projeto --- .htaccess | 6 ++++++ _bootstrap.php | 13 +++++++++++++ api/api.php | 5 +++++ class/DB.class.php | 30 ++++++++++++++++++++++++++++++ class/Rest.class.php | 10 ++++++++++ db.sq3 | Bin 0 -> 3072 bytes 6 files changed, 64 insertions(+) create mode 100644 .htaccess create mode 100644 _bootstrap.php create mode 100644 api/api.php create mode 100644 class/DB.class.php create mode 100644 class/Rest.class.php create mode 100644 db.sq3 diff --git a/.htaccess b/.htaccess new file mode 100644 index 00000000..b464b96f --- /dev/null +++ b/.htaccess @@ -0,0 +1,6 @@ + +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule api/(.*)$ api/api.php?url=$1 [QSA,NC,L] + diff --git a/_bootstrap.php b/_bootstrap.php new file mode 100644 index 00000000..581a33b4 --- /dev/null +++ b/_bootstrap.php @@ -0,0 +1,13 @@ +setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + self::$Conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + return self::$Conn; + } + + public static function getAll($table) + { + $sql = "SELECT * FROM {$table}"; + + $rs = self::getInstance()->query($sql); + + $output = array(); + if($rs) { + $output = $rs->fetchAll(); + } + return $output; + } +} diff --git a/class/Rest.class.php b/class/Rest.class.php new file mode 100644 index 00000000..6b4e90b2 --- /dev/null +++ b/class/Rest.class.php @@ -0,0 +1,10 @@ +6Hd$M`*?veB2V za=XkUjrZ3B)8)8J;x0?eT?4jm3H*w{(~099R;$kQnWkfo3gcX5a^u{K>wa(8hhgu0 z(1%SDe3k%9HQu3unPP&f1x#@Psnn3>oYzex5FV(C@2T>`R;yO1@ z_oLzz3d;DlnT+!FlM^nf_M1UHERM#C(L|0=fX2Co1Sd4-8qA&wQ$5m5V5X^<)q!%2 z>PNk`_1`SL5Ob_q0+zrZC2;HPlS;*DdQo)Aag1uZzK=d#7w0HuTpq_#bl>1{wL};+ gPa8ql2-*+?oha;t?Qc?rb=^X2LrcIC_y+_&0O2=|0ssI2 literal 0 HcmV?d00001 From 388db044eb2aecaaeca1974c19801694032ace29 Mon Sep 17 00:00:00 2001 From: Kleber C Batista Date: Thu, 9 Feb 2017 16:02:30 -0200 Subject: [PATCH 2/5] Created endpoint 'resources' and all related manipulation methods. --- .htaccess | 9 +- _bootstrap.php | 23 ++- api/api.php | 5 - class/DB.class.php | 83 +++++++++- class/Request.class.php | 76 +++++++++ class/Rest.class.php | 10 -- class/model/Resources.class.php | 267 ++++++++++++++++++++++++++++++++ db.sq3 | Bin 3072 -> 3072 bytes index.php | 11 ++ 9 files changed, 460 insertions(+), 24 deletions(-) delete mode 100644 api/api.php create mode 100644 class/Request.class.php delete mode 100644 class/Rest.class.php create mode 100644 class/model/Resources.class.php create mode 100644 index.php diff --git a/.htaccess b/.htaccess index b464b96f..8330727e 100644 --- a/.htaccess +++ b/.htaccess @@ -1,6 +1,7 @@ -RewriteEngine On -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule api/(.*)$ api/api.php?url=$1 [QSA,NC,L] + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule (.*)$ index.php?url=$1 [QSA,NC,L] + # RewriteRule api/(.*)$ api/api.php?url=$1 [QSA,NC,L] diff --git a/_bootstrap.php b/_bootstrap.php index 581a33b4..f7506d11 100644 --- a/_bootstrap.php +++ b/_bootstrap.php @@ -1,13 +1,28 @@ isDot()) + continue; + + if($item->isDir()) + recursive_autoloader($class, $item->getPathname() . DIRECTORY_SEPARATOR); + } + + $class_file = $path . "{$class}.class.php"; if(file_exists($class_file)) { require_once $class_file; } } + +spl_autoload_register('recursive_autoloader'); diff --git a/api/api.php b/api/api.php deleted file mode 100644 index a8fc25e3..00000000 --- a/api/api.php +++ /dev/null @@ -1,5 +0,0 @@ -fetchAll(); } + return $output; } + + public static function getOneByIdFrom($table, $id) + { + $sql = "SELECT * FROM {$table} WHERE id = {$id}"; + + $rs = self::getInstance()->query($sql); + $item = $rs->fetch(); + + if(!$item) + return array(); + + return $item; + } + + public static function saveAt($table, array $data) + { + if(!$data['id']) + return self::insert($table, $data); + + return self::update($table, $data); + } + + public function removeFrom($table, array $data) + { + $sql = "DELETE FROM {$table} WHERE id = ?"; + + $st = self::getInstance()->prepare($sql); + $st->execute(array($data['id'])); + + return $data; + } + + private function insert($table, array $data) + { + $fields = array(); + $values = array(); + + foreach($data as $field => $value) { + if(in_array($field, array('id'))) + continue; + + $fields[] = $field; + $values[] = $value; + + $placeholders[] = '?'; + } + + $fields = implode(', ', $fields); + $placeholders = implode(', ', $placeholders); + + $sql = "INSERT INTO {$table} ({$fields}) VALUES ({$placeholders})"; + $st = self::getInstance()->prepare($sql); + $st->execute($values); + + $data['id'] = self::getInstance()->lastInsertId(); + + return $data; + } + + private function update($table, array $data) + { + $clauses = array(); + $values = array(); + + foreach($data as $field => $value) { + if(in_array($field, array('id'))) + continue; + + $clauses[] = "{$field} = ?"; + $values[] = $value; + } + + $clauses = implode(', ', $clauses); + + $sql = "UPDATE {$table} SET {$clauses} WHERE id = {$data['id']}"; + $st = self::getInstance()->prepare($sql); + $st->execute($values); + + return $data; + } } diff --git a/class/Request.class.php b/class/Request.class.php new file mode 100644 index 00000000..ed96a87e --- /dev/null +++ b/class/Request.class.php @@ -0,0 +1,76 @@ +verb = $_SERVER['REQUEST_METHOD']; + } + + public function handle() + { + // Receives only JSON content + if(array_key_exists('CONTENT_TYPE', $_SERVER) && $_SERVER['CONTENT_TYPE'] !== 'application/json') { + $this->sendResponse(400); + } + + $this->resolveUrl(); + + if(!in_array($this->getEndpoint(), self::$valid_endpoints)) + $this->sendResponse(400); + + // Create object and manipulate response + $class = ucwords($this->endpoint); + new $class($this); + } + + public function sendResponse($status_code, $response_body=null) + { + header("HTTP/1.0 {$status_code}", true, $status_code); + + if($response_body) + echo json_encode($response_body); + + die; + } + + private function resolveUrl() + { + if(!array_key_exists('url', $_GET)) { + $this->sendResponse(404); + } + + $pieces = explode('/', $_GET['url']); + + if(count($pieces) > 2) { + $this->sendResponse(400); + } + + $this->endpoint = $pieces[0]; + if(array_key_exists(1, $pieces)) { + $this->id = $pieces[1]; + } + } + + // GETTERS + public function getEndpoint() + { + return $this->endpoint; + } + + public function getId() + { + return $this->id; + } + + public function getVerb() + { + return $this->verb; + } +} diff --git a/class/Rest.class.php b/class/Rest.class.php deleted file mode 100644 index 6b4e90b2..00000000 --- a/class/Rest.class.php +++ /dev/null @@ -1,10 +0,0 @@ -getVerb()) { + case 'GET': + if($Request->getId()) { + if(!is_numeric($Request->getId())) { + $Request->sendResponse(400); + } + + $resource = $this->getById($Request->getId()); + + if(!$resource) + $Request->sendResponse(404); + + $Request->sendResponse(200, $resource); + } else { + $resources = $this->getAll(); + + $Request->sendResponse(200, $resources); + } + + break; + + case 'POST': + if($Request->getId()) { + $Request->sendResponse(400); + } + $resource = $this->createFrom($Request); + + $Request->sendResponse(200, $resource); + break; + + case 'PATCH': + if(!$Request->getId() || !is_numeric($Request->getId())) { + $Request->sendResponse(400); + } + + $resource = $this->updateFrom($Request); + + $Request->sendResponse(200, $resource); + break; + + case 'DELETE': + if(!$Request->getId() || !is_numeric($Request->getId())) { + $Request->sendResponse(400); + } + + $resource = $this->deleteFrom($Request); + + $Request->sendResponse(200, $resource); + break; + + default: + $Request->sendResponse(404); + } + } + + public function toArray() + { + return array( + 'id' => $this->getId(), + 'name' => $this->getName(), + 'age'=> $this->getAge(), + 'email' => $this->getEmail(), + 'department' => $this->getDepartment(), + 'salary' => $this->getSalary(), + 'created_at' => $this->getCreatedAt(), + 'updated_at' => $this->getUpdatedAt() + ); + } + + private function getAll() + { + $rs = DB::getAllFrom(self::$table); + + if(!$rs) + return array(); + + return $rs; + } + + private function getById($id) + { + return DB::getOneByIdFrom(self::getTable(), $id); + } + + private function createFrom($Request) + { + $input_data = json_decode(file_get_contents('php://input'), true); + + $required_keys = array('name', 'age', 'email', 'department', 'salary'); + if(array_keys($input_data) !== $required_keys) { + $Request->sendResponse(400); + } + + $this->setName($input_data['name']); + $this->setAge($input_data['age']); + $this->setEmail($input_data['email']); + $this->setDepartment($input_data['department']); + $this->setSalary($input_data['salary']); + + $now = date('Y-m-d H:i:s'); + $this->setCreatedAt($now); + $this->setUpdatedAt($now); + + return $this->save(); + } + + private function updateFrom($Request) + { + $input_data = json_decode(file_get_contents('php://input'), true); + + $valid_keys = array('name', 'age', 'email', 'department', 'salary'); + if(array_diff(array_keys($input_data), $valid_keys)) { + $Request->sendResponse(400); + } + + $resource = $this->getById($Request->getId()); + if(!$resource) { + $Request->sendResponse(404); + } + + $this->setId($resource['id']); + + $this->setName($resource['name']); + if(array_key_exists('name', $input_data)) + $this->setName($input_data['name']); + + $this->setAge($resource['age']); + if(array_key_exists('age', $input_data)) + $this->setAge($input_data['age']); + + $this->setEmail($resource['email']); + if(array_key_exists('email', $input_data)) + $this->setEmail($input_data['email']); + + $this->setDepartment($resource['department']); + if(array_key_exists('department', $input_data)) + $this->setDepartment($input_data['department']); + + $this->setSalary($resource['salary']); + if(array_key_exists('salary', $input_data)) + $this->setSalary($input_data['salary']); + + $this->setCreatedAt($resource['created_at']); + $this->setUpdatedAt(date('Y-m-d H:i:s')); + + return $this->save(); + } + + private function deleteFrom($Request) + { + $resource = $this->getById($Request->getId()); + if(!$resource) { + $Request->sendResponse(404); + } + + return DB::removeFrom(self::getTable(), $resource); + } + + private function save() + { + return DB::saveAt(self::getTable(), $this->toArray()); + } + + // SETTERS + public function setId($id) + { + $this->id = $id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function setAge($age) + { + $this->age = $age; + } + + public function setEmail($email) + { + $this->email = $email; + } + + public function setDepartment($department) + { + $this->department = $department; + } + + public function setSalary($salary) + { + $this->salary = $salary; + } + + public function setCreatedAt($created_at) + { + $this->created_at = $created_at; + } + + public function setUpdatedAt($updated_at) + { + $this->updated_at = $updated_at; + } + + // GETTERS + public static function getTable() + { + return self::$table; + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function getAge() + { + return $this->age; + } + + public function getEmail() + { + return $this->email; + } + + public function getDepartment() + { + return $this->department; + } + + public function getSalary() + { + return $this->salary; + } + + public function getCreatedAt() + { + return $this->created_at; + } + + public function getUpdatedAt() + { + return $this->updated_at; + } +} diff --git a/db.sq3 b/db.sq3 index eaf45002914e26bd29da611e09d486a2bcae0a35..c8532f803d0708b05b8a5d1a43edcd7b9bc5de51 100644 GIT binary patch literal 3072 zcmeH{&ui2`6vt0>LGz$$&Zb)Ic2S@f_qVWYh{@xZ?{7ylQ5YSw@2}= z|A7bZJ$dkKPx@c<)UyZgPDINHF165`8F+b*H}8Gs@r9Qc&!$WPnU}JlibQA)VT@8j z5JHYN`0bz*6u6`UuM@m}-Er`nEk*Y@ zNT|z3W>B98*4FdMczZ&&#}B3xa-JqG%Lx+-79dF_SwZEJ9Ke!@QW4Q`-Y0?PJydXV0IOd z6^xg558dnq-g*>`rP^<#UPS#(v$CkDSl-osoEJ>6T1l!{Dbn}vj!qvRZ-jnuf8d7$ ze?)>f38Ewl{!B%qBp&TpJ#-64j>b=^WOYq%>qTz8`NFhZXgKA6e6!Z6H7D<4Rnlhandle(); +} catch(Exception $e) { + $req->sendResponse(500); +} From ec2464d0b66309af55b3175fc63543e1214cd845 Mon Sep 17 00:00:00 2001 From: Kleber C Batista Date: Thu, 9 Feb 2017 21:59:45 -0200 Subject: [PATCH 3/5] Added 'users'endpoint and related manipulation methods. Added composer.json. --- .gitignore | 2 + _bootstrap.php | 20 ++++- class/DB.class.php | 24 ++++- class/Model.class.php | 100 +++++++++++++++++++++ class/Request.class.php | 2 +- class/model/Resources.class.php | 105 ++++------------------ class/model/Users.class.php | 154 ++++++++++++++++++++++++++++++++ composer.json | 5 ++ db.sq3 | Bin 3072 -> 3072 bytes index.php | 2 +- 10 files changed, 317 insertions(+), 97 deletions(-) create mode 100644 .gitignore create mode 100644 class/Model.class.php create mode 100644 class/model/Users.class.php create mode 100644 composer.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d1502b08 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +vendor/ +composer.lock diff --git a/_bootstrap.php b/_bootstrap.php index f7506d11..ddb1e0d1 100644 --- a/_bootstrap.php +++ b/_bootstrap.php @@ -3,8 +3,11 @@ if(!defined('ROOT_DIR')) define('ROOT_DIR', dirname(__FILE__)); -if(!defined('API_DIR')) - define('API_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'api' . DIRECTORY_SEPARATOR); +if(!defined('VENDOR_DIR')) + define('VENDOR_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR); + +// Composer +require VENDOR_DIR . 'autoload.php'; function recursive_autoloader($class, $path=ROOT_DIR) { @@ -24,5 +27,16 @@ function recursive_autoloader($class, $path=ROOT_DIR) require_once $class_file; } } - spl_autoload_register('recursive_autoloader'); + +// $teste = array( +// 'id' => 1, +// 'name' => 'Teste', +// 'email' => 'teste@it.com' +// ); +// $token = JWT::encode($teste, '12121980kcb'); +// +// print_r($token); +// +// $teste = JWT::decode($token, '12121980kcb', array('HS256')); +// print_r($teste); die; diff --git a/class/DB.class.php b/class/DB.class.php index a2ce6998..59e86636 100644 --- a/class/DB.class.php +++ b/class/DB.class.php @@ -15,9 +15,11 @@ private static function getInstance() return self::$Conn; } - public static function getAllFrom($table) + public static function getAllFrom($table, array $fields) { - $sql = "SELECT * FROM {$table}"; + $fields = implode(', ', $fields); + + $sql = "SELECT {$fields} FROM {$table}"; $rs = self::getInstance()->query($sql); @@ -29,9 +31,11 @@ public static function getAllFrom($table) return $output; } - public static function getOneByIdFrom($table, $id) + public static function getOneByIdFrom($table, array $fields, $id) { - $sql = "SELECT * FROM {$table} WHERE id = {$id}"; + $fields = implode(', ', $fields); + + $sql = "SELECT {$fields} FROM {$table} WHERE id = {$id}"; $rs = self::getInstance()->query($sql); $item = $rs->fetch(); @@ -42,6 +46,18 @@ public static function getOneByIdFrom($table, $id) return $item; } + public static function getOneByField($table, $field, $value) + { + $sql = "SELECT * FROM {$table} WHERE {$field} = ?"; + $st = self::getInstance()->prepare($sql); + $st->execute(array($value)); + + if(!$st) + return false; + + return $st->fetch(); + } + public static function saveAt($table, array $data) { if(!$data['id']) diff --git a/class/Model.class.php b/class/Model.class.php new file mode 100644 index 00000000..c7b79b21 --- /dev/null +++ b/class/Model.class.php @@ -0,0 +1,100 @@ +getVerb()) { + case 'GET': + if($Request->getId()) { + if(!is_numeric($Request->getId())) { + $Request->sendResponse(400); + } + + $resource = $this->getById($Request->getId()); + + if(!$resource) + $Request->sendResponse(404); + + $Request->sendResponse(200, $resource); + } else { + $resources = $this->getAll(); + + $Request->sendResponse(200, $resources); + } + + break; + + case 'POST': + if($Request->getId()) { + $Request->sendResponse(400); + } + $resource = $this->createFrom($Request); + + $Request->sendResponse(200, $resource); + break; + + case 'PATCH': + if(!$Request->getId() || !is_numeric($Request->getId())) { + $Request->sendResponse(400); + } + + $resource = $this->updateFrom($Request); + + $Request->sendResponse(200, $resource); + break; + + case 'DELETE': + if(!$Request->getId() || !is_numeric($Request->getId())) { + $Request->sendResponse(400); + } + + $resource = $this->deleteFrom($Request); + + $Request->sendResponse(200, $resource); + break; + + default: + $Request->sendResponse(404); + } + } + + protected function getAll() + { + $rs = DB::getAllFrom(static::getTable(), static::getFields()); + + if(!$rs) + return array(); + + return $rs; + } + + protected function getById($id) + { + return DB::getOneByIdFrom(static::getTable(), static::getFields(), $id); + } + + abstract public function toArray(); + + abstract protected function createFrom($Request); + + abstract protected function updateFrom($Request); + + abstract protected function deleteFrom($Request); + + abstract protected function save(); + + // GETTERS + public static function getTable() + { + return static::$table; + } + + public static function getFields() + { + return static::$fields; + } +} diff --git a/class/Request.class.php b/class/Request.class.php index ed96a87e..4d7cfcac 100644 --- a/class/Request.class.php +++ b/class/Request.class.php @@ -2,7 +2,7 @@ class Request { - private static $valid_endpoints = array('resources'); + private static $valid_endpoints = array('resources', 'users'); private $endpoint; private $id; diff --git a/class/model/Resources.class.php b/class/model/Resources.class.php index deea0311..ad3392bc 100644 --- a/class/model/Resources.class.php +++ b/class/model/Resources.class.php @@ -1,74 +1,18 @@ getVerb()) { - case 'GET': - if($Request->getId()) { - if(!is_numeric($Request->getId())) { - $Request->sendResponse(400); - } - - $resource = $this->getById($Request->getId()); - - if(!$resource) - $Request->sendResponse(404); - - $Request->sendResponse(200, $resource); - } else { - $resources = $this->getAll(); - - $Request->sendResponse(200, $resources); - } - - break; - - case 'POST': - if($Request->getId()) { - $Request->sendResponse(400); - } - $resource = $this->createFrom($Request); - - $Request->sendResponse(200, $resource); - break; - - case 'PATCH': - if(!$Request->getId() || !is_numeric($Request->getId())) { - $Request->sendResponse(400); - } - - $resource = $this->updateFrom($Request); - - $Request->sendResponse(200, $resource); - break; - - case 'DELETE': - if(!$Request->getId() || !is_numeric($Request->getId())) { - $Request->sendResponse(400); - } - - $resource = $this->deleteFrom($Request); - - $Request->sendResponse(200, $resource); - break; - - default: - $Request->sendResponse(404); - } - } + protected static $table = 'resources'; + protected static $fields = array('*'); // visible_fields + + protected $id; + protected $name; + protected $age; + protected $email; + protected $department; + protected $salary; + protected $created_at; + protected $updated_at; public function toArray() { @@ -84,27 +28,12 @@ public function toArray() ); } - private function getAll() - { - $rs = DB::getAllFrom(self::$table); - - if(!$rs) - return array(); - - return $rs; - } - - private function getById($id) - { - return DB::getOneByIdFrom(self::getTable(), $id); - } - - private function createFrom($Request) + protected function createFrom($Request) { $input_data = json_decode(file_get_contents('php://input'), true); $required_keys = array('name', 'age', 'email', 'department', 'salary'); - if(array_keys($input_data) !== $required_keys) { + if(!$input_data || array_keys($input_data) !== $required_keys) { $Request->sendResponse(400); } @@ -121,7 +50,7 @@ private function createFrom($Request) return $this->save(); } - private function updateFrom($Request) + protected function updateFrom($Request) { $input_data = json_decode(file_get_contents('php://input'), true); @@ -163,7 +92,7 @@ private function updateFrom($Request) return $this->save(); } - private function deleteFrom($Request) + protected function deleteFrom($Request) { $resource = $this->getById($Request->getId()); if(!$resource) { @@ -173,7 +102,7 @@ private function deleteFrom($Request) return DB::removeFrom(self::getTable(), $resource); } - private function save() + protected function save() { return DB::saveAt(self::getTable(), $this->toArray()); } diff --git a/class/model/Users.class.php b/class/model/Users.class.php new file mode 100644 index 00000000..5717ee56 --- /dev/null +++ b/class/model/Users.class.php @@ -0,0 +1,154 @@ + $this->getId(), + 'name' => $this->getName(), + 'email' => $this->getEmail(), + 'password' => $this->getPassword(), + 'auth_token' => $this->getAuthToken() + ); + } + + protected function createFrom($Request) + { + $input_data = json_decode(file_get_contents('php://input'), true); + + // Validate + $required_keys = array('name', 'email', 'password'); + if(!$input_data || array_keys($input_data) !== $required_keys) { + $Request->sendResponse(400); + } + if(!filter_var($input_data['email'], FILTER_VALIDATE_EMAIL)) { + echo "INVALID EMAIL"; + $Request->sendResponse(400); + } + if(DB::getOneByField(self::getTable(), 'email', $input_data['email'])) { + $Request->sendResponse(409); + } + if(strlen($input_data['password']) < 6) { + $Request->sendResponse(400); + } + + $this->setName($input_data['name']); + $this->setEmail($input_data['email']); + $this->setPassword($input_data['password']); + + return $this->save(); + } + + protected function updateFrom($Request) + { + $input_data = json_decode(file_get_contents('php://input'), true); + + $valid_keys = array('name', 'email'); + if(array_diff(array_keys($input_data), $valid_keys)) { + $Request->sendResponse(400); + } + + $resource = DB::getOneByIdFrom(static::getTable(), array('*'), $Request->getId()); + if(!$resource) { + $Request->sendResponse(404); + } + + $this->setId($resource['id']); + + $this->setName($resource['name']); + if(array_key_exists('name', $input_data)) + $this->setName($input_data['name']); + + $this->setEmail($resource['email']); + if(array_key_exists('email', $input_data)) + $this->setEmail($input_data['email']); + + $this->setPassword($resource['password']); + $this->setAuthToken($resource['auth_token']); + + return $this->save(); + } + + protected function deleteFrom($Request) + { + $resource = $this->getById($Request->getId()); + if(!$resource) { + $Request->sendResponse(404); + } + + return DB::removeFrom(self::getTable(), $resource); + } + + protected function save() + { + $item = DB::saveAt(self::getTable(), $this->toArray()); + + foreach(array_diff(array_keys($item), self::getFields()) as $skip) { + unset($item[$skip]); + } + + return $item; + } + + // SETTERS + public function setId($id) + { + $this->id = $id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function setEmail($email) + { + $this->email = $email; + } + + public function setPassword($password) + { + $this->password = sha1($password); + } + + public function setAuthToken($token) + { + $this->tokens[] = $token; + } + + // GETTERS + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function getEmail() + { + return $this->email; + } + + public function getPassword() + { + return $this->password; + } + + public function getAuthToken() + { + return $this->auth_token; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..f9edafbc --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "firebase/php-jwt": "^2.2.0" + } +} diff --git a/db.sq3 b/db.sq3 index c8532f803d0708b05b8a5d1a43edcd7b9bc5de51..3a8ce52d54e5fcd3b945740400a7b600e0dacb68 100644 GIT binary patch delta 460 zcmZvYze^)Q6vt<0LZYIFRxyY;oyr-R-Pzqq0zy_VUZEgJssoYP*%=M4o47kMiiJN~ zL6r11{sl)G5w5b;zr)JH)YYN7-HKxu!~$jt&xVe##L zEJe`x0PxZH_DXq!*y?Yy@va!`wpw!4K%&f z;z`nJ#sNO$vDoGDoaH$4IBIGfwZm`$^S0jI*3CT`VJ(j|4y5Ak@N+xgdxV}bnt_)9 z-JwZ4N508^ZQoW^5X+2xS zWJO{XNg11^3MCcdeuK8qFenUlU$dLJa>ceQmfKy;J^UCv19brUhdPM#CWad5^cT#6 znb|E+mx-3xAw>5FtJ={0^DZS83s`E@CDOJC6+}{1W<&~0DWRmJO3L&-BMS;?%33IW m8Sf4BE5ddHw=5J>%oV0n@&iGbt!yPs-(e%fW;PQ%Q0zZ_K!Rle delta 122 zcmZpWXpop7Ehx{xz`zW}j6j-mqK+|8Q1{~wULcQ==`aJ+Vdj&Y6handle(); } catch(Exception $e) { - $req->sendResponse(500); + $req->sendResponse(500, $e->getMessage()); } From 438f90912a8483fd144cbdf6406d6a87caf23cd8 Mon Sep 17 00:00:00 2001 From: Kleber C Batista Date: Thu, 9 Feb 2017 22:48:14 -0200 Subject: [PATCH 4/5] Implemented user JWT generation and storing under the database. --- class/Model.class.php | 11 +++-------- class/Request.class.php | 5 ++++- class/model/Users.class.php | 23 ++++++++++++++++++----- db.sq3 | Bin 3072 -> 3072 bytes 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/class/Model.class.php b/class/Model.class.php index c7b79b21..2eed1530 100644 --- a/class/Model.class.php +++ b/class/Model.class.php @@ -32,9 +32,8 @@ public function __construct($Request) if($Request->getId()) { $Request->sendResponse(400); } - $resource = $this->createFrom($Request); - $Request->sendResponse(200, $resource); + $this->createFrom($Request); break; case 'PATCH': @@ -42,9 +41,7 @@ public function __construct($Request) $Request->sendResponse(400); } - $resource = $this->updateFrom($Request); - - $Request->sendResponse(200, $resource); + $this->updateFrom($Request); break; case 'DELETE': @@ -52,9 +49,7 @@ public function __construct($Request) $Request->sendResponse(400); } - $resource = $this->deleteFrom($Request); - - $Request->sendResponse(200, $resource); + $this->deleteFrom($Request); break; default: diff --git a/class/Request.class.php b/class/Request.class.php index 4d7cfcac..52c26c76 100644 --- a/class/Request.class.php +++ b/class/Request.class.php @@ -30,9 +30,12 @@ public function handle() new $class($this); } - public function sendResponse($status_code, $response_body=null) + public function sendResponse($status_code, $response_body=null, $additional_headers=array()) { header("HTTP/1.0 {$status_code}", true, $status_code); + foreach ($additional_headers as $header) { + header($header); + } if($response_body) echo json_encode($response_body); diff --git a/class/model/Users.class.php b/class/model/Users.class.php index 5717ee56..0feb92c3 100644 --- a/class/model/Users.class.php +++ b/class/model/Users.class.php @@ -46,7 +46,16 @@ protected function createFrom($Request) $this->setEmail($input_data['email']); $this->setPassword($input_data['password']); - return $this->save(); + // Token data + $token_data = array( + 'name' => $this->getName(), + 'email' => $this->getEmail() + ); + $this->setAuthToken(JWT::encode($token_data, '12b80k12c')); + + $resource = $this->save(); + + $Request->sendResponse(200, $resource, array("x-auth: {$this->getAuthToken()}")); } protected function updateFrom($Request) @@ -76,7 +85,9 @@ protected function updateFrom($Request) $this->setPassword($resource['password']); $this->setAuthToken($resource['auth_token']); - return $this->save(); + $resource = $this->save(); + + $Request->sendResponse(200, $resource); } protected function deleteFrom($Request) @@ -86,7 +97,9 @@ protected function deleteFrom($Request) $Request->sendResponse(404); } - return DB::removeFrom(self::getTable(), $resource); + $resource = DB::removeFrom(self::getTable(), $resource); + + $Request->sendResponse(200, $resource); } protected function save() @@ -121,9 +134,9 @@ public function setPassword($password) $this->password = sha1($password); } - public function setAuthToken($token) + public function setAuthToken($auth_token) { - $this->tokens[] = $token; + $this->auth_token = $auth_token; } // GETTERS diff --git a/db.sq3 b/db.sq3 index 3a8ce52d54e5fcd3b945740400a7b600e0dacb68..bcad84883af65c82a5aaef6f38f2de0ea6246f48 100644 GIT binary patch delta 182 zcmZpWXpop7%@{LL#+fl@W5N=4Hb!P+24>^Uf*hL6T#a6g>>l?3 From 59b5afa077135fa8d439234b2e77b766dc501ed4 Mon Sep 17 00:00:00 2001 From: Kleber C Batista Date: Fri, 10 Feb 2017 00:39:32 -0200 Subject: [PATCH 5/5] Implemented 'login' endpoint and authentication. --- _bootstrap.php | 15 +++------------ class/DB.class.php | 21 ++++++++++++++++++--- class/Model.class.php | 23 +++++++++++++---------- class/Request.class.php | 33 ++++++++++++++++++++++++++++++++- class/model/Users.class.php | 13 ++++--------- db.sq3 | Bin 3072 -> 3072 bytes index.php | 2 +- 7 files changed, 71 insertions(+), 36 deletions(-) diff --git a/_bootstrap.php b/_bootstrap.php index ddb1e0d1..4fffbac1 100644 --- a/_bootstrap.php +++ b/_bootstrap.php @@ -6,6 +6,9 @@ if(!defined('VENDOR_DIR')) define('VENDOR_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR); +if(!defined('API_JWT_SECRET')) + define('API_JWT_SECRET', '12121980kcb'); + // Composer require VENDOR_DIR . 'autoload.php'; @@ -28,15 +31,3 @@ function recursive_autoloader($class, $path=ROOT_DIR) } } spl_autoload_register('recursive_autoloader'); - -// $teste = array( -// 'id' => 1, -// 'name' => 'Teste', -// 'email' => 'teste@it.com' -// ); -// $token = JWT::encode($teste, '12121980kcb'); -// -// print_r($token); -// -// $teste = JWT::decode($token, '12121980kcb', array('HS256')); -// print_r($teste); die; diff --git a/class/DB.class.php b/class/DB.class.php index 59e86636..a63a516f 100644 --- a/class/DB.class.php +++ b/class/DB.class.php @@ -46,11 +46,19 @@ public static function getOneByIdFrom($table, array $fields, $id) return $item; } - public static function getOneByField($table, $field, $value) + public static function getOneByField($table, array $fields) { - $sql = "SELECT * FROM {$table} WHERE {$field} = ?"; + $clauses = array(); + $values = array(); + foreach($fields as $field => $value) { + $clauses[] = "{$field} = ?"; + $values[] = $value; + } + $clauses = implode(' AND ', $clauses); + + $sql = "SELECT * FROM {$table} WHERE {$clauses}"; $st = self::getInstance()->prepare($sql); - $st->execute(array($value)); + $st->execute($values); if(!$st) return false; @@ -124,4 +132,11 @@ private function update($table, array $data) return $data; } + + public function saveAuthTokenFor($table, $id, $auth_token) + { + $sql = "UPDATE {$table} SET auth_token = ? WHERE id = ?"; + $st = self::getInstance()->prepare($sql); + $st->execute(array($auth_token, $id)); + } } diff --git a/class/Model.class.php b/class/Model.class.php index 2eed1530..f538f5d5 100644 --- a/class/Model.class.php +++ b/class/Model.class.php @@ -25,7 +25,6 @@ public function __construct($Request) $Request->sendResponse(200, $resources); } - break; case 'POST': @@ -72,15 +71,19 @@ protected function getById($id) return DB::getOneByIdFrom(static::getTable(), static::getFields(), $id); } - abstract public function toArray(); - - abstract protected function createFrom($Request); - - abstract protected function updateFrom($Request); - - abstract protected function deleteFrom($Request); - - abstract protected function save(); + protected function generateAuthToken($id, $name, $email) + { + // Token data + $token_data = array( + 'id' => $id, + 'name' => $name, + 'email' => $email + ); + $auth_token = JWT::encode($token_data, API_JWT_SECRET); + DB::saveAuthTokenFor(Users::getTable(), $id, $auth_token); + + return $auth_token; + } // GETTERS public static function getTable() diff --git a/class/Request.class.php b/class/Request.class.php index 52c26c76..6bb58edc 100644 --- a/class/Request.class.php +++ b/class/Request.class.php @@ -2,7 +2,7 @@ class Request { - private static $valid_endpoints = array('resources', 'users'); + private static $valid_endpoints = array('resources', 'users', 'login'); private $endpoint; private $id; @@ -15,12 +15,14 @@ public function __construct() public function handle() { + // Receives only JSON content if(array_key_exists('CONTENT_TYPE', $_SERVER) && $_SERVER['CONTENT_TYPE'] !== 'application/json') { $this->sendResponse(400); } $this->resolveUrl(); + $this->validateAuthToken(); if(!in_array($this->getEndpoint(), self::$valid_endpoints)) $this->sendResponse(400); @@ -61,6 +63,35 @@ private function resolveUrl() } } + private function validateAuthToken() + { + // Set bypass authentication + if(($this->getEndpoint() === 'users' && !$this->getId()) || $this->getEndpoint() === 'login') { + return; + } + + if(!array_key_exists('HTTP_X_AUTH', $_SERVER)) + $this->sendResponse(401); + + $auth_token = $_SERVER['HTTP_X_AUTH']; + try{ + $token_data = JWT::decode($auth_token, API_JWT_SECRET, array('HS256')); + } catch(Exception $e) { + $this->sendResponse(401); + } + + $fields = array( + 'name' => $token_data->name, + 'email' => $token_data->email, + 'auth_token' => $auth_token, + ); + $resource = DB::getOneByField(Users::getTable(), $fields); + + if(!$resource) + $this->sendResponse(401); + } + + // GETTERS public function getEndpoint() { diff --git a/class/model/Users.class.php b/class/model/Users.class.php index 0feb92c3..dd49324b 100644 --- a/class/model/Users.class.php +++ b/class/model/Users.class.php @@ -35,7 +35,7 @@ protected function createFrom($Request) echo "INVALID EMAIL"; $Request->sendResponse(400); } - if(DB::getOneByField(self::getTable(), 'email', $input_data['email'])) { + if(DB::getOneByField(self::getTable(), array('email' => $input_data['email']))) { $Request->sendResponse(409); } if(strlen($input_data['password']) < 6) { @@ -46,16 +46,11 @@ protected function createFrom($Request) $this->setEmail($input_data['email']); $this->setPassword($input_data['password']); - // Token data - $token_data = array( - 'name' => $this->getName(), - 'email' => $this->getEmail() - ); - $this->setAuthToken(JWT::encode($token_data, '12b80k12c')); - $resource = $this->save(); - $Request->sendResponse(200, $resource, array("x-auth: {$this->getAuthToken()}")); + $auth_token = $this->generateAuthToken($resource['id'], $resource['name'], $resource['email']); + + $Request->sendResponse(200, $resource, array("x-auth: {$auth_token}")); } protected function updateFrom($Request) diff --git a/db.sq3 b/db.sq3 index bcad84883af65c82a5aaef6f38f2de0ea6246f48..0f23eec2dc82832cab9b12c667372f16fa7d327c 100644 GIT binary patch delta 102 zcmZpWXpop7&6qq<#+fmBW5P0a7Ig+@_057DvdmnKVT|kylIpQdrju>Cler6`oITAv zvs^QMoF~ucHq&-ZjHoDe$+z$}b1!#_$SbOd^e_u^tuXe^&987zjx;MZGc-#z^74&$ GDF*#HG;F201?h}xbm*i-Xo|PM9k(rfU9_(Kc2mrd&90mXY diff --git a/index.php b/index.php index 1b52ed3e..8d96ad01 100644 --- a/index.php +++ b/index.php @@ -7,5 +7,5 @@ try { $req->handle(); } catch(Exception $e) { - $req->sendResponse(500, $e->getMessage()); + $req->sendResponse(500); }