diff --git a/app/Controllers/BlockController.php b/app/Controllers/BlockController.php index 63613f7..c68cb71 100644 --- a/app/Controllers/BlockController.php +++ b/app/Controllers/BlockController.php @@ -5,6 +5,7 @@ use App\Models\Block; use Core\Http\Controllers\Controller; use Core\Http\Request; +use Exception; use function array_map; use function is_null; @@ -30,8 +31,7 @@ public function create(Request $request): void { $image = ($_FILES['photo'] ?? null); $params = $request->getBody(); - unset($params['PHPSESSID']); - $block = new Block($params); + $block = new Block(['name' => $params['name']]); if ($block->isValid()) { if ($block->save()) { @@ -111,10 +111,20 @@ public function imageUpdate(Request $request): void public function destroy(Request $request): void { - $params = $request->getParams(); - $block = Block::findById($params['id']); - $block->destroy(); + try { + $params = $request->getParams(); + $block = Block::findById($params['id']); + + if (!$block) { + echo json_encode(['error' => 'Bloco não encontrado']); + return; + } + + $block->destroy(); - echo json_encode(['success' => 'deletado com sucesso']); + echo json_encode(['success' => 'Deletado com sucesso']); + } catch (Exception $e) { + echo json_encode(['error' => $e->getMessage()]); + } } } diff --git a/app/Controllers/ClassRoomController.php b/app/Controllers/ClassRoomController.php index 0e5473a..30b8758 100644 --- a/app/Controllers/ClassRoomController.php +++ b/app/Controllers/ClassRoomController.php @@ -8,7 +8,9 @@ use Core\Database\Database; use Core\Http\Controllers\Controller; use Core\Http\Request; +use Exception; use PDO; +use PDOException; use function is_null; use function json_encode; @@ -88,10 +90,20 @@ public function update(Request $request): void public function destroy(Request $request): void { - $params = $request->getParams(); - $block = Block::findById($params['id']); - $block->destroy(); + try { + $params = $request->getParams(); + $classroom = ClassRoom::findById($params['id']); + + if (!$classroom) { + echo json_encode(['error' => 'Bloco não encontrado']); + return; + } - echo json_encode(['success' => 'deletado com sucesso']); + $classroom->destroy(); + + echo json_encode(['success' => 'Deletado com sucesso']); + } catch (Exception $e) { + echo json_encode(['error' => $e->getMessage()]); + } } } diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php index 5aed60c..3322be8 100644 --- a/app/Controllers/HomeController.php +++ b/app/Controllers/HomeController.php @@ -21,7 +21,6 @@ class HomeController extends Controller { public function index(Request $request): void { - $params = $request->getParams(); $date = date('Y-m-d'); if (isset($params['date'])) { diff --git a/app/Controllers/SchedulesController.php b/app/Controllers/SchedulesController.php index b0b645d..50538b1 100644 --- a/app/Controllers/SchedulesController.php +++ b/app/Controllers/SchedulesController.php @@ -3,6 +3,7 @@ namespace App\Controllers; use App\Enums\RolesEnum; +use App\Models\Block; use App\Models\ClassRoom; use App\Models\Roles; use App\Models\Schedules; @@ -52,9 +53,8 @@ public function index(): void public function byProfessorId(Request $request): void { - - $id = $request->getParams()['id']; - $allSchedules = Schedules::byProfessorId($id); + $userId = (Auth::user()->id); + $allSchedules = Schedules::byProfessorId($userId); $schedulesArray = array_map(function ($schedule) { return [ 'id' => $schedule->id, @@ -259,9 +259,21 @@ public function roomChange(Request $request): void public function delete(Request $request): void { - $params = $request->getParams(); - $subject = Schedules::findById($params['id']); - $subject->destroy(); + try { + $params = $request->getParams(); + $schedule = Schedules::findById($params['id']); + + if (!$schedule) { + echo json_encode(['error' => 'Bloco não encontrado']); + return; + } + + $schedule->destroy(); + + echo json_encode(['success' => 'Deletado com sucesso']); + } catch (Exception $e) { + echo json_encode(['error' => $e->getMessage()]); + } } public function validatesDateConflict(Schedules $schedule): bool diff --git a/app/Controllers/SubjectController.php b/app/Controllers/SubjectController.php index 5722c35..0c9ddaf 100644 --- a/app/Controllers/SubjectController.php +++ b/app/Controllers/SubjectController.php @@ -2,9 +2,11 @@ namespace App\Controllers; +use App\Models\Block; use App\Models\Subject; use Core\Http\Controllers\Controller; use Core\Http\Request; +use Exception; use function array_map; use function is_null; @@ -84,10 +86,20 @@ public function update(Request $request): void public function destroy(Request $request): void { - $params = $request->getParams(); - $subject = Subject::findById($params['id']); - $subject->destroy(); + try { + $params = $request->getParams(); + $subject = Subject::findById($params['id']); + + if (!$subject) { + echo json_encode(['error' => 'Bloco não encontrado']); + return; + } + + $subject->destroy(); - echo json_encode(['success' => 'deletado com sucesso']); + echo json_encode(['success' => 'Deletado com sucesso']); + } catch (Exception $e) { + echo json_encode(['error' => $e->getMessage()]); + } } } diff --git a/app/Controllers/UsersController.php b/app/Controllers/UsersController.php index e4207e8..2cfd6e7 100644 --- a/app/Controllers/UsersController.php +++ b/app/Controllers/UsersController.php @@ -6,9 +6,11 @@ use App\Models\User; use Core\Http\Controllers\Controller; use Core\Http\Request; +use Firebase\JWT\JWT; use Lib\Authentication\Auth; use function array_map; +use function getenv; use function hash; use function json_encode; use function password_hash; @@ -39,10 +41,18 @@ public function login(Request $request): void if ($user && $user->authenticate($params['password'])) { Auth::login($user); + $payload = [ + "iss" => "http://localhost", + "aud" => "http://localhost", + "iat" => time(), + "exp" => time() + (60 * 60), + "user_id" => $user->id + ]; + $token = JWT::encode($payload, $_ENV['PASSWORD_KEY_HASH'], 'HS256'); echo json_encode([ 'success' => 'Logado com sucesso', 'role' => $user->roleName(), - 'token' => $user->id + 'token' => $token ]); } else { http_response_code(400); diff --git a/app/Middleware/AdminRole.php b/app/Middleware/AdminRole.php index 8e83191..d4133e0 100644 --- a/app/Middleware/AdminRole.php +++ b/app/Middleware/AdminRole.php @@ -2,19 +2,62 @@ namespace App\Middleware; +use App\Models\User; use Core\Exceptions\HTTPException; use Core\Http\Middleware\Middleware; use Core\Http\Request; +use Exception; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; use Lib\Authentication\Auth; +use function dd; +use function getenv; +use function http_response_code; +use function json_encode; +use function str_replace; + class AdminRole implements Middleware { public function handle(Request $request): void { - if (Auth::user()->role_id != 1) { + $headers = getallheaders(); + if (!isset($headers['Authorization'])) { + http_response_code(401); + echo json_encode(["error" => "Token não fornecido"]); + exit(); + } + + $token = str_replace('Bearer ', '', $headers['Authorization']); + $data = $this->validatesToken($token); + $user = User::findById($data['user_id']); + + if ($user->role_id != 1) { header('Content-Type: application/json', true, 401); echo json_encode(['error' => 'Acesso restrito a admnistradores']); exit; } } + + /** + * + * @param string $token + * @return array|null + */ + + public function validatesToken(string $token): ?array + { + $key = $_ENV['PASSWORD_KEY_HASH'] ?? getenv('PASSWORD_KEY_HASH'); + + if (!$key) { + return null; + } + + try { + $decoded = JWT::decode($token, new Key($key, 'HS256')); + return (array) $decoded; + } catch (Exception $e) { + return null; + } + } } diff --git a/app/Middleware/Authenticate.php b/app/Middleware/Authenticate.php index d56a757..cf24da1 100644 --- a/app/Middleware/Authenticate.php +++ b/app/Middleware/Authenticate.php @@ -5,16 +5,54 @@ use Core\Exceptions\HTTPException; use Core\Http\Middleware\Middleware; use Core\Http\Request; +use Exception; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; use Lib\Authentication\Auth; +use function dd; +use function glob; + class Authenticate implements Middleware { public function handle(Request $request): void { - if (!Auth::check()) { - header('Content-Type: application/json', true, 401); - echo json_encode(['error' => 'Você precisa estar autenticado para acessar esta página.']); - exit; + $headers = getallheaders(); + if (!isset($headers['Authorization'])) { + http_response_code(401); + echo json_encode(["error" => "Token não fornecido"]); + exit(); + } + + $token = str_replace('Bearer ', '', $headers['Authorization']); + $data = $this->validatesToken($token); + + if (!$data) { + http_response_code(401); + echo json_encode(["error" => "Token inválido"]); + exit(); + } + + // echo json_encode(["message" => "Autorizado", "user" => $data]); + } + /** + * + * @param string $token + * @return array|null + */ + public function validatesToken(string $token): ?array + { + $key = $_ENV['PASSWORD_KEY_HASH'] ?? getenv('PASSWORD_KEY_HASH'); + + if (!$key) { + return null; + } + + try { + $decoded = JWT::decode($token, new Key($key, 'HS256')); + return (array) $decoded; + } catch (Exception $e) { + return null; } } } diff --git a/composer.json b/composer.json index e56b1d7..7818e4c 100644 --- a/composer.json +++ b/composer.json @@ -17,5 +17,8 @@ "phpunit/phpunit": "^11.1", "squizlabs/php_codesniffer": "*", "phpstan/phpstan": "^1.10" + }, + "require": { + "firebase/php-jwt": "^6.11" } } diff --git a/composer.lock b/composer.lock index 611309d..35c001a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,72 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1f3adee7b02b489d60204f84df44dd59", - "packages": [], + "content-hash": "67405feb9efac0986df93248e20cc662", + "packages": [ + { + "name": "firebase/php-jwt", + "version": "v6.11.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "8f718f4dfc9c5d5f0c994cdfd103921b43592712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/8f718f4dfc9c5d5f0c994cdfd103921b43592712", + "reference": "8f718f4dfc9c5d5f0c994cdfd103921b43592712", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.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.11.0" + }, + "time": "2025-01-23T05:11:06+00:00" + } + ], "packages-dev": [ { "name": "myclabs/deep-copy", @@ -1784,5 +1848,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.2.0" } diff --git a/config/bootstrap.php b/config/bootstrap.php index 54829de..a8bd25a 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -4,13 +4,12 @@ header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization'); header('Access-Control-Allow-Credentials: true'); - +header('Access-Control-Max-Age: 86400'); if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { http_response_code(200); exit(); } - require __DIR__ . '/../vendor/autoload.php'; use Core\Env\EnvLoader; diff --git a/config/routes.php b/config/routes.php index 814ac75..74de9b4 100644 --- a/config/routes.php +++ b/config/routes.php @@ -23,85 +23,82 @@ Route::get('/blocks', [BlockController::class, 'index']) ->name('blocks'); +Route::get('/classrooms', [ClassRoomController::class, 'index']) + ->name('classroom'); +Route::get('/classrooms/{id}', [ClassRoomController::class, 'show']) + ->name('classroom.show'); + Route::middleware('auth')->group(function () { + Route::get('/user-subjects', [UserSubjectsController::class, 'index']) + ->name('subject.professor'); + Route::get('/logout', [UsersController::class, 'destroy']) - ->name('users.logout'); + ->name('users.logout'); Route::get('/dashboard', [DashboardController::class, 'index']) - ->name('dashboard'); + ->name('dashboard'); Route::get('/users/professors', [UsersController::class, 'professors']) - ->name('users.professors'); - - + ->name('users.professors'); -// Route::middleware('role:admin')->group(function () { + Route::middleware('role:admin')->group(function () { Route::post('/blocks', [BlockController::class, 'create']) - ->name('blocks.create'); + ->name('blocks.create'); Route::post('/blocks/image-update/{id}', [BlockController::class, 'imageUpdate']) ->name('blocks.image'); Route::put('/blocks/{id}', [BlockController::class, 'update']) - ->name('blocks.update'); + ->name('blocks.update'); Route::delete('/blocks/{id}', [BlockController::class, 'destroy']) - ->name('blocks.destroy'); + ->name('blocks.destroy'); Route::post('/classrooms', [ClassRoomController::class, 'create']) - ->name('classroom.create'); + ->name('classroom.create'); Route::put('/classrooms/{id}', [ClassRoomController::class, 'update']) - ->name('classroom.update'); + ->name('classroom.update'); Route::delete('/classrooms/{id}', [ClassRoomController::class, 'destroy']) - ->name('classroom.destroy'); - + ->name('classroom.destroy'); Route::post('/subjects', [SubjectController::class, 'create']) - ->name('subject.create'); + ->name('subject.create'); Route::put('/subjects/{id}', [SubjectController::class, 'update']) - ->name('subject.update'); + ->name('subject.update'); Route::delete('/subjects/{id}', [SubjectController::class, 'destroy']) - ->name('subject.destroy'); + ->name('subject.destroy'); Route::post('/user-subjects', [UserSubjectsController::class, 'addSubjectToProfessor']) - ->name('subject.professor.create'); + ->name('subject.professor.create'); Route::delete('/user-subjects/{id}', [UserSubjectsController::class, 'delete']) - ->name('subject.professor.delete'); + ->name('subject.professor.delete'); Route::post('/schedules', [SchedulesController::class, 'create']) - ->name('schedules.create'); + ->name('schedules.create'); Route::delete('/schedules/{id}', [SchedulesController::class, 'delete']) - ->name('schedules.delete'); -// }); + ->name('schedules.delete'); + }); + // Outras rotas protegidas Route::get('/schedules', [SchedulesController::class, 'index']) - ->name('schedules.index'); + ->name('schedules.index'); Route::get('/schedules/professor/{id}', [SchedulesController::class, 'byProfessorId']) ->name('schedules.userId'); Route::get('/schedules/{id}', [SchedulesController::class, 'show']) - ->name('schedules.show'); + ->name('schedules.show'); Route::post('/schedules/cancel', [SchedulesController::class, 'creatreCancelSchedule']) - ->name('schedules.cancel'); + ->name('schedules.cancel'); Route::delete('/schedules/cancel/{id}', [SchedulesController::class, 'deleteCancelSchedule']) - ->name('schedules.cancel.delete'); + ->name('schedules.cancel.delete'); Route::post('/schedules/change', [SchedulesController::class, 'roomChange']) - ->name('schedules.post'); + ->name('schedules.post'); Route::get('/schedules/exceptions', [SchedulesController::class, 'exceptions']) ->name('schedules.exceptions'); - Route::get('/blocks/{id}', [BlockController::class, 'show']) - ->name('blocks.show'); - - Route::get('/classrooms', [ClassRoomController::class, 'index']) - ->name('classroom'); - Route::get('/classrooms/{id}', [ClassRoomController::class, 'show']) - ->name('classroom.show'); + ->name('blocks.show'); Route::get('/subjects', [SubjectController::class, 'index']) - ->name('subject'); + ->name('subject'); Route::get('/subjects/{id}', [SubjectController::class, 'show']) - ->name('subject.show'); - - Route::get('/user-subjects', [UserSubjectsController::class, 'index']) - ->name('subject.professor'); + ->name('subject.show'); }); diff --git a/lib/Authentication/Auth.php b/lib/Authentication/Auth.php index e435022..6d2850f 100644 --- a/lib/Authentication/Auth.php +++ b/lib/Authentication/Auth.php @@ -3,7 +3,15 @@ namespace Lib\Authentication; use App\Models\User; +use Exception; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; +use function dd; +use function getallheaders; +use function getenv; +use function http_response_code; +use function json_encode; use function str_replace; class Auth @@ -15,23 +23,41 @@ public static function login($user): void public static function user(): ?User { - if (isset($_SESSION['user']['id'])) { - $id = $_SESSION['user']['id']; - return User::findById($id); + $headers = getallheaders(); + if (!isset($headers['Authorization'])) { + // http_response_code(401); + // echo json_encode(["error" => "Token não fornecido"]); + return null; + exit(); + } + + $token = str_replace('Bearer ', '', $headers['Authorization']); + $data = self::validatesToken($token); // Alterado para 'self::' porque 'validatesToken' é estático + if (isset($data['user_id'])) { + return User::findById($data['user_id']); } return null; } - public static function check(): bool + public static function validatesToken($token) { - return true; - if ($_SESSION['user']['id'] == str_replace("Bearer ", "", $_SERVER["HTTP_AUTHORIZATION"])) { - return true; + $key = $_ENV['PASSWORD_KEY_HASH'] ?? getenv('PASSWORD_KEY_HASH'); + + if (!$key) { + return null; + } + + try { + $decoded = JWT::decode($token, new Key($key, 'HS256')); + return (array) $decoded; + } catch (Exception $e) { + return null; } - return false; -// $token = str_replace("Bearer ", "", $_SERVER["HTTP_AUTHORIZATION"]); -// return $_SESSION['user']['id'] == $token; -// return isset($_SESSION['user']['id']) && self::user() !== null; + } + + public static function check(): bool + { + return self::user() !== null; } public static function logout(): void diff --git a/tests/Unit/Models/SchedulesTest.php b/tests/Unit/Models/SchedulesTest.php index a3fa883..5be6788 100644 --- a/tests/Unit/Models/SchedulesTest.php +++ b/tests/Unit/Models/SchedulesTest.php @@ -226,7 +226,6 @@ public function setUp(): void public function test_should_create_new_schedules(): void { - dd('Schedules::all()'); $this->assertCount(9, Schedules::all()); } @@ -257,6 +256,7 @@ public function teste_should_change_room(): void 'is_canceled' => 0, 'date' => $this->date, 'exceptional_day' => 1, + 'block_id' => $schedule->block_id ]); $changeSchedule->save(); $schedules = Schedules::withCancelAndSubstitutionsCurrentWeek($this->date);