diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 9f335d2..fdc2e9b 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -35,6 +35,7 @@ class Filters extends BaseFilters 'forcehttps' => ForceHTTPS::class, 'pagecache' => PageCache::class, 'performance' => PerformanceMetrics::class, + 'auth' => \App\Filters\AuthFilter::class, ]; /** @@ -70,7 +71,7 @@ class Filters extends BaseFilters */ public array $globals = [ 'before' => [ - 'cors' + 'cors', // 'honeypot', // 'csrf', // 'invalidchars', diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 1f0fecf..a779411 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -10,9 +10,21 @@ $routes->options('(:any)', function() { }); $routes->get('/', 'Home::index'); -$routes->post('/auth/login/', 'Auth::login'); -$routes->post('/auth/change_pass/', 'Auth::change_pass'); -$routes->post('/auth/register/', 'Auth::register'); +//PUNYA GUS INI TEMP +$routes->get('/api/v1/emr/lab/list-new', 'NUHATEMP::index'); +$routes->post('/api/v1/emr/lab/insert', 'NUHATEMP::create'); +$routes->post('/api/v1/emr/lab/update-validasi', 'NUHATEMP::update'); +$routes->post('/api/v1/emr/lab/detail', 'NUHATEMP::detail'); + +$routes->group('api', ['filter' => 'auth'], function($routes) { + $routes->get('coba-auth', 'Auth::coba'); +}); + $routes->post('/api/auth/login', 'Auth::login'); + $routes->post('/api/auth/change_pass', 'Auth::change_pass'); + $routes->post('/api/auth/register', 'Auth::register'); + $routes->get('/api/auth/check', 'Auth::checkAuth'); + $routes->post('/api/auth/logout', 'Auth::logout'); + $routes->get('/api/patient', 'Patient::index'); $routes->post('/api/patient', 'Patient::create'); diff --git a/app/Controllers/Auth.php b/app/Controllers/Auth.php index ff41e0d..bede8b8 100644 --- a/app/Controllers/Auth.php +++ b/app/Controllers/Auth.php @@ -4,16 +4,79 @@ namespace App\Controllers; use CodeIgniter\API\ResponseTrait; use CodeIgniter\Controller; -use \Firebase\JWT\JWT; + +use Firebase\JWT\JWT; +use Firebase\JWT\Key; +use Firebase\JWT\ExpiredException; +use Firebase\JWT\SignatureInvalidException; +use Firebase\JWT\BeforeValidException; +use CodeIgniter\Cookie\Cookie; class Auth extends Controller { use ResponseTrait; + // ok public function __construct() { $this->db = \Config\Database::connect(); } + // ok + public function checkAuth() { + $token = $this->request->getCookie('token'); + $key = getenv('JWT_SECRET'); + + // Jika token FE tidak ada langsung kabarkan failed + if (!$token) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'No token found' + ], 401); + } + + try { + // Decode Token dengan Key yg ada di .env + $decodedPayload = JWT::decode($token, new Key($key, 'HS256')); + + return $this->respond([ + 'status' => 'success', + 'message' => 'Authenticated', + 'data' => $decodedPayload + ], 200); + + } catch (ExpiredException $e) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Token expired', + 'data' => [] + ], 401); + + } catch (SignatureInvalidException $e) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Invalid token signature', + 'data' => [] + ], 401); + + } catch (BeforeValidException $e) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Token not valid yet', + 'data' => [] + ], 401); + + } catch (\Exception $e) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Invalid token: ' . $e->getMessage(), + 'data' => [] + ], 401); + } + } + + // ok public function login() { + + // Ambil dari JSON Form dan Key .env $username = $this->request->getVar('username'); $password = $this->request->getVar('password'); $key = getenv('JWT_SECRET'); @@ -21,115 +84,147 @@ class Auth extends Controller { if (!$username) { return $this->fail('Username required.', 400); } - - $sql = "select * from users where username=".$this->db->escape($username); + + $sql = "SELECT * FROM users WHERE username=" . $this->db->escape($username); $query = $this->db->query($sql); $row = $query->getRowArray(); if (!$row) { - return $this->fail('User not found.', 401); // Use 401 for authentication failures + return $this->fail('User not found.', 401); } - + if (!password_verify($password, $row['password'])) { return $this->fail('Invalid password.', 401); } - - // JWT payload + + // Buat JWT payload $payload = [ + 'userid' => $row['id'], 'username' => $row['username'], - 'exp' => time() + 3600 + 'exp' => time() + 86400 // 1 hari ]; try { + // Melakukan Hash terhadap Payload dengan Kunci .env menggunakan Algortima HMAC + SHA-256 $jwt = JWT::encode($payload, $key, 'HS256'); } catch (Exception $e) { return $this->fail('Error generating JWT: ' . $e->getMessage(), 500); } - // Update last_login - //$this->userModel->update($user['id'], ['lastlogin' => date('Y-m-d H:i:s')]); + // Kirim Respon ke HttpOnly yg akan disimpan di browser dan tidak akan dapat diakses oleh siapapun + $this->response->setCookie([ + 'name' => 'token', // nama token + 'value' => $jwt, // value dari jwt yg sudah di hash + 'expire' => 86400, // 1 hari + 'path' => '/', // valid untuk semua path + 'secure' => true, // set true kalau sudah HTTPS + 'httponly' => true, // dipakai agar cookie berikut tidak dapat diakses oleh javascript + 'samesite' => Cookie::SAMESITE_NONE + ]); - $response = [ - 'message' => 'Login successful', - 'token' => $jwt, - ]; - return $this->respond($response); + // Response tanpa token di body + return $this->respond([ + 'status' => 'success', + 'message' => 'Login successful' + ], 200); } - public function change_pass() { - $db = \Config\Database::connect(); - $username = $this->request->getJsonVar('username'); - $password = $this->request->getJsonVar('password'); - $password = password_hash($password, PASSWORD_DEFAULT); - - $master = $this->request->getJsonVar('master'); - $masterkey = getenv('masterkey'); + // ok + public function logout() { + // Definisikan ini pada cookies browser, harus sama dengan cookies login + return $this->response->setCookie([ + 'name' => 'token', + 'value' => '', + 'expire' => time() - 3600, + 'path' => '/', + 'secure' => true, + 'httponly' => true, + 'samesite' => Cookie::SAMESITE_NONE - if($master != $masterkey) { - return $this->fail('Invalid master key.', 401); - } - - $sql = "update users set password='$password' where username='$username'"; - $query = $db->query($sql); - $response = [ - 'message' => "Password Changed for $username" - ]; - return $this->respond($response); + ])->setJSON([ + 'status' => 'success', + 'message' => 'Logout successful' + ], 200); } + // ok public function register() { + $username = $this->request->getJsonVar('username'); $password = $this->request->getJsonVar('password'); - $password = password_hash($password, PASSWORD_DEFAULT); - $master = $this->request->getJsonVar('master'); - $masterkey = getenv('MASTERKEY'); - - if($master != $masterkey) { - return $this->fail('Invalid master key.', 401); - } - - $sql = "insert into users(username, password) values('$username', '$password')"; - $this->db->query($sql); - $response = [ - 'message' => "user $username created" - ]; - return $this->respondCreated($response); - } - - public function checkAuth() { - $authorizationHeader = $this->request->getHeader('Authorization'); - - if (!$authorizationHeader) { - return $this->fail('Authorization header is missing', 401); - } - - $authHeaderValue = $authorizationHeader->getValue(); - - if (empty($authHeaderValue)) { - return $this->fail('Authorization header is empty', 401); - } - - // Extract the token from the "Bearer " format - if (strpos($authHeaderValue, 'Bearer ') === 0) { - $token = substr($authHeaderValue, 7); - } else { - $token = $authHeaderValue; // Assume the header contains only the token + // Validasi + if (empty($username) || empty($password)) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Username and password are required' + ], 400); // Gunakan 400 Bad Request } + $hashedPassword = password_hash($password, PASSWORD_DEFAULT); + $sql = "INSERT INTO users(username, password) VALUES(?, ?)"; try { - $decoded = JWT::decode($token, $this->key, ['HS256']); // Use the Key object - // You can now access user data from $decoded - $response = [ - 'message' => 'Authentication successful', - 'user' => $decoded, // return the decoded token - ]; - return $this->respond($response); + // Jalankan kueri dan binding data secara terpisah + $this->db->query($sql, [$username, $hashedPassword]); - } catch (Exception $e) { - return $this->fail('Invalid token: ' . $e->getMessage(), 401); + // Respon sukses jika kueri berhasil + return $this->respond([ + 'status' => 'success', + 'message' => 'User ' . $username . ' successfully created.' + ], 201); // Kode 201 Created sudah benar untuk resource baru + + } catch (\Exception $e) { + + // Tangani error lain-lain + return $this->respond([ + 'status' => 'error', + 'message' => 'Failed to create user. Please try again later.' + ], 500); // Kode 500 Internal Server Error untuk masalah di server } + + } -} \ No newline at end of file + // public function change_pass() { + // $db = \Config\Database::connect(); + // $username = $this->request->getJsonVar('username'); + // $password = $this->request->getJsonVar('password'); + // $password = password_hash($password, PASSWORD_DEFAULT); + + // $master = $this->request->getJsonVar('master'); + // $masterkey = getenv('masterkey'); + + // if($master != $masterkey) { + // return $this->fail('Invalid master key.', 401); + // } + + // $sql = "update users set password='$password' where username='$username'"; + // $query = $db->query($sql); + // $response = [ + // 'message' => "Password Changed for $username" + // ]; + // return $this->respond($response); + // } + + public function coba() { + + $token = $this->request->getCookie('token'); + $key = getenv('JWT_SECRET'); + + // Decode Token dengan Key yg ada di .env + $decodedPayload = JWT::decode($token, new Key($key, 'HS256')); + + return $this->respond([ + 'status' => 'success', + 'message' => 'Authenticated', + 'data' => $decodedPayload + ], 200); + + // return $this->respond([ + // 'status' => 'success', + // 'message' => 'Already Login' + // ],200); + } + +} diff --git a/app/Controllers/NUHATEMP.php b/app/Controllers/NUHATEMP.php new file mode 100644 index 0000000..ccbddde --- /dev/null +++ b/app/Controllers/NUHATEMP.php @@ -0,0 +1,197 @@ +db = \Config\Database::connect(); + } + + // OK - Done + public function index() { + + $valid_from = $this->request->getVar('valid_from'); + $valid_to = $this->request->getVar('valid_to'); + try { + // Data pasien ditemukan dan mengembalikan - success 200 + return $this->respond([ + "response" => [ + "list" => [ + [ + "tgl" => "2023-07-31T00:12:00Z", + "no_lab" => 153304, + "no_rm" => "001063898", + "nama" => "WAHYUDI", + "tgl_lahir" => "1976-05-15T00:00:00Z", + "jenis_kelamin" => "Laki-laki", + "umur" => "47 tahun, 2 bulan, 15 hari", + "alamat" => "KP GEBANG RT 01/03 SANGIANG JAYA PERIUK", + "ruang" => "", + "kelas" => "", + "status" => "BPJS", + "dokter_pengirim" => "dr. Arie Asnafi, Sp.U", + "jenis_lab" => "pk", + "lis_id" => "2307310001", + "id_ruangan" => "1", + "nama_ruangan" => "Flamboyan", + "id_asuransi" => "2", + "nama_asuransi" => "BPJS KESEHATAN", + "cito" => false, + "list_test" => [ + [ + "detail_id" => 265833, + "no_lab" => 153304, + "test_id" => 72, + "nama_test" => "Glukosa Sewaktu", + "jenis_lab" => "pk", + "jenis_test" => "t", + "detail_test" => [] + ] + ] + ], + [ + "tgl" => "2023-07-31T00:53:00Z", + "no_lab" => 153307, + "no_rm" => "001301669", + "nama" => "RAFFASYAH ALKHALIFI PUTRA H", + "tgl_lahir" => "2017-11-28T00:00:00Z", + "jenis_kelamin" => "Laki-laki", + "umur" => "5 tahun, 8 bulan, 2 hari", + "alamat" => "JL TARUMANEGARA I / 4 RT 04 RW 022", + "ruang" => "", + "kelas" => "", + "status" => "BPJS", + "dokter_pengirim" => "dr. Arif Budiman, Sp.A (K)", + "jenis_lab" => "pk", + "lis_id" => "2307310004", + "id_ruangan" => "1", + "nama_ruangan" => "Flamboyan", + "id_asuransi" => "2", + "nama_asuransi" => "BPJS KESEHATAN", + "list_test" => [ + [ + "detail_id" => 265836, + "no_lab" => 153307, + "test_id" => 31, + "nama_test" => "Darah Lengkap", + "jenis_lab" => "pk", + "jenis_test" => "p", + "detail_test" => [ + ["paket_id"=>31,"index"=>1,"spasi"=>"0","test_id"=>16,"nama_test"=>"Hemoglobin"], + ["paket_id"=>31,"index"=>2,"spasi"=>"0","test_id"=>26,"nama_test"=>"Leukosit"], + ["paket_id"=>31,"index"=>3,"spasi"=>"0","test_id"=>28,"nama_test"=>"Eritrosit"], + ["paket_id"=>31,"index"=>4,"spasi"=>"0","test_id"=>29,"nama_test"=>"Hematokrit"], + ["paket_id"=>31,"index"=>5,"spasi"=>"0","test_id"=>30,"nama_test"=>"Trombosit"], + ["paket_id"=>31,"index"=>6,"spasi"=>"0","test_id"=>32,"nama_test"=>"Hitung Jenis"], + ["paket_id"=>31,"index"=>7,"spasi"=>"0","test_id"=>33,"nama_test"=>"Eosinofil"], + ["paket_id"=>31,"index"=>8,"spasi"=>"0","test_id"=>34,"nama_test"=>"Basofil"], + ["paket_id"=>31,"index"=>9,"spasi"=>"0","test_id"=>35,"nama_test"=>"Netrofil Batang"], + ["paket_id"=>31,"index"=>10,"spasi"=>"0","test_id"=>36,"nama_test"=>"Netrofil Segmen"], + ["paket_id"=>31,"index"=>11,"spasi"=>"0","test_id"=>37,"nama_test"=>"Limfosit"], + ["paket_id"=>31,"index"=>12,"spasi"=>"0","test_id"=>38,"nama_test"=>"Monosit"], + ["paket_id"=>31,"index"=>13,"spasi"=>"0","test_id"=>39,"nama_test"=>"Laju Endap Darah"] + ] + ] + ] + ] + ] + ], + "metadata" => [ + "message" => "Ok", + "code" => 200 + ] + ], 200); + + } catch (\Exception $e) { + // Error Server Mengembalikan 500 + return $this->failServerError('Something went wrong.'.$e->getMessage()); + } + } + + // OK - Done + public function create() { + try { + // Data pasien ditemukan dan mengembalikan - success 200 + return $this->respond([ + "message"=> "Ok", + 'status' => 200 + ], 200); + + } catch (\Exception $e) { + // Error Server Mengembalikan 500 + return $this->failServerError('Something went wrong.'.$e->getMessage()); + } + } + + // OK - Done + public function update() { + try { + // Data pasien ditemukan dan mengembalikan - success 200 + return $this->respond([ + "message"=> "Ok", + 'status' => 200 + ], 200); + + } catch (\Exception $e) { + // Error Server Mengembalikan 500 + return $this->failServerError('Something went wrong.'.$e->getMessage()); + } + } + + // OK - Done + public function detail() { + try { + // Data pasien ditemukan dan mengembalikan - success 200 + return $this->respond([ + "response" => [ + "tgl" => "2024-03-08T09:37:00Z", + "no_lab" => 3021, + "no_rm" => "000264395", + "nama" => "CANDY RAMADONA, AN", + "tgl_lahir" => "2007-09-02", + "jenis_kelamin" => "Perempuan", + "umur" => "2023 tahun, 2 bulan, 7 hari", + "alamat" => "PERUM PONDOK JAGUNG BLOK AC.45 001/004", + "ruang" => "", + "kelas" => "", + "status" => "ASURANSI SINAR MAS, PT", + "id_dokter_pengirim" => 2, + "dokter_pengirim" => "dr.Mila Agustia.Sp.A", + "jenis_lab" => "pk", + "lis_id" => "240308208", + "id_ruangan" => "1", + "nama_ruangan" => "DAMAR", + "id_asuransi" => "118", + "nama_asuransi" => "ASURANSI SINAR MAS, PT", + "cito" => false, + "list_test" => [ + [ + "detail_id" => 4981, + "no_lab" => 3021, + "test_id" => 145, + "nama_test" => "Urin Lengkap (Paket)", + "jenis_lab" => "", + "jenis_test" => "", + "detail_test" => null + ] + ] + ], + "metadata" => [ + "message" => "Ok", + "code" => 200 + ] + ], 200); + + } catch (\Exception $e) { + // Error Server Mengembalikan 500 + return $this->failServerError('Something went wrong.'.$e->getMessage()); + } + } + + +} \ No newline at end of file diff --git a/app/Filters/AuthFilter.php b/app/Filters/AuthFilter.php new file mode 100644 index 0000000..ad9d297 --- /dev/null +++ b/app/Filters/AuthFilter.php @@ -0,0 +1,50 @@ +getCookie('token'); // ambil dari cookie + + // Kalau tidak ada token + if (!$token) { + return Services::response() + ->setStatusCode(401) + ->setJSON([ + 'status' => 'failed', + 'message' => 'Unauthorized: Token not found' + ]); + } + + try { + // Decode JWT : jika error maka akan mentrigger catch + $decoded = JWT::decode($token, new Key($key, 'HS256')); + + // Kalau mau, bisa inject user info ke request + // $request->userData = $decoded; + + } catch (\Exception $e) { + return Services::response() + ->setStatusCode(401) + ->setJSON([ + 'status' => 'failed', + 'message' => 'Unauthorized: ' . $e->getMessage() + ]); + } + } + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + // Tidak perlu apa-apa + } +} diff --git a/app/Filters/Cors.php b/app/Filters/Cors.php index e989e43..c18b5aa 100644 --- a/app/Filters/Cors.php +++ b/app/Filters/Cors.php @@ -22,7 +22,8 @@ class Cors implements FilterInterface if (in_array($origin, $this->allowedOrigins)) { $response->setHeader('Access-Control-Allow-Origin', $origin); $response->setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS'); - $response->setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With'); + $response->setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, Accept, Origin, Cache-Control, Pragma'); + // $response->setHeader('Access-Control-Allow-Headers', '*'); $response->setHeader('Access-Control-Allow-Credentials', 'true'); }