From d1280644ad476a4139c35a75db3f1f9159adad83 Mon Sep 17 00:00:00 2001 From: habibie11 Date: Wed, 24 Jun 2026 16:35:19 +0700 Subject: [PATCH 1/5] fix: generate token sinkronisasi --- app/Http/Controllers/Api/TokenController.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/Http/Controllers/Api/TokenController.php b/app/Http/Controllers/Api/TokenController.php index 7ebf8176a8..73d8bfe0e3 100644 --- a/app/Http/Controllers/Api/TokenController.php +++ b/app/Http/Controllers/Api/TokenController.php @@ -32,6 +32,7 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; +use App\Models\SettingAplikasi; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Config; use Tymon\JWTAuth\Facades\JWTAuth; @@ -50,6 +51,12 @@ public function index() $user = Auth::user(); $token = JWTAuth::fromUser($user); + // Simpan token ke das_setting agar middleware token.registered dapat memvalidasinya + SettingAplikasi::updateOrCreate( + ['key' => 'api_key_opendk'], + ['value' => $token] + ); + // Return the token in a response return response()->json(['token' => $token]); } From f8ef2c1f8d1a37fe5527690b08de00fa1807eb94 Mon Sep 17 00:00:00 2001 From: habibie11 Date: Wed, 24 Jun 2026 19:48:46 +0700 Subject: [PATCH 2/5] fix sinkronisasi file --- app/Services/FileUploadService.php | 67 +++++++++++++++++------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/app/Services/FileUploadService.php b/app/Services/FileUploadService.php index 1754ff45e4..6f2daca86d 100644 --- a/app/Services/FileUploadService.php +++ b/app/Services/FileUploadService.php @@ -19,19 +19,28 @@ public function uploadSecure( ): string { // 1. Validate MIME type $this->validateMimeType($file, $allowedMimes); - + // 2. Validate file size (KB) $this->validateFileSize($file, $maxSize); - + // 3. Sanitize directory to prevent path traversal $directory = $this->sanitizeDirectoryPath($directory); - + // 4. Generate safe filename $safeFileName = $this->generateSafeFileName($file); - + // 5. Store file securely - $path = $file->storeAs($directory, $safeFileName, 'public'); - + // Sebelum : $path = $file->storeAs($directory, $safeFileName, 'public'); + // Menggunakan manual stream alih-alih $file->storeAs() karena pada + // environment tertentu (seperti Laragon/Windows), $file->getRealPath() + // me-return false untuk file di C:\Windows\Temp yang menyebabkan ValueError. + $stream = fopen($file->getPathname(), 'r'); + $path = trim($directory.'/'.$safeFileName, '/'); + Storage::disk('public')->put($path, $stream); + if (is_resource($stream)) { + fclose($stream); + } + return $path; } @@ -45,13 +54,13 @@ public function uploadMultiple( int $maxSize = 2048 ): array { $uploadedPaths = []; - + foreach ($files as $file) { if ($file instanceof UploadedFile) { $uploadedPaths[] = $this->uploadSecure($file, $directory, $allowedMimes, $maxSize); } } - + return $uploadedPaths; } @@ -63,7 +72,7 @@ public function delete(string $path): bool if (Storage::disk('public')->exists($path)) { return Storage::disk('public')->delete($path); } - + return false; } @@ -75,12 +84,12 @@ protected function validateMimeType(UploadedFile $file, array $allowedMimes): vo if (empty($allowedMimes)) { return; } - + $fileMime = $file->getMimeType(); - - if (!in_array($fileMime, $allowedMimes)) { + + if (! in_array($fileMime, $allowedMimes)) { throw new \InvalidArgumentException( - "File type not allowed. Allowed types: " . implode(', ', $allowedMimes) + 'File type not allowed. Allowed types: '.implode(', ', $allowedMimes) ); } } @@ -91,7 +100,7 @@ protected function validateMimeType(UploadedFile $file, array $allowedMimes): vo protected function validateFileSize(UploadedFile $file, int $maxSize): void { $fileSizeInKB = $file->getSize() / 1024; - + if ($fileSizeInKB > $maxSize) { throw new \InvalidArgumentException( "File size exceeds maximum allowed size of {$maxSize}KB" @@ -106,14 +115,14 @@ protected function sanitizeDirectoryPath(string $directory): string { // Remove any path traversal attempts $directory = str_replace(['../', '..\\', './', '.\\'], '', $directory); - + // Additional sanitization to prevent directory traversal $directory = preg_replace('#/\.{2}/#', '/', $directory); // Prevent /../ $directory = preg_replace('#\\\\\.{2}\\\\#', '\\', $directory); // Prevent \..\ - + return $directory; } - + /** * Sanitize file extension to prevent malicious extensions */ @@ -138,7 +147,7 @@ protected function sanitizeExtension(string $extension): string return $sanitized !== '' ? $sanitized : 'tmp'; } - + /** * Generate safe filename */ @@ -148,17 +157,17 @@ protected function generateSafeFileName(UploadedFile $file): string $originalName = $file->getClientOriginalName(); // Reject if original name contains traversal or contains directory parts if (str_contains($originalName, '..') || basename($originalName) !== $originalName || preg_match('/[\\\\\/]/', $originalName)) { - throw new \InvalidArgumentException("File name contains path traversal attempts"); + throw new \InvalidArgumentException('File name contains path traversal attempts'); } - + // Generate unique hash-based filename $extension = $file->getClientOriginalExtension(); $timestamp = time(); $random = Str::random(16); - + // Make sure extension is safe too $extension = $this->sanitizeExtension($extension); - + return "{$timestamp}_{$random}.{$extension}"; } @@ -167,15 +176,15 @@ protected function generateSafeFileName(UploadedFile $file): string */ public static function getAllowedMimes(string $category): array { - return match($category) { + return match ($category) { 'image' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], - 'document' => ['application/pdf', 'application/msword', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], - 'spreadsheet' => ['application/vnd.ms-excel', - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'text/csv'], + 'document' => ['application/pdf', 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], + 'spreadsheet' => ['application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'text/csv'], 'archive' => ['application/zip', 'application/x-zip-compressed'], default => [], }; } -} \ No newline at end of file +} From 43bced3f5f8211a7abbf29bd438c88a2a59dde05 Mon Sep 17 00:00:00 2001 From: habibie11 Date: Thu, 25 Jun 2026 11:06:16 +0700 Subject: [PATCH 3/5] kembalikan insert ke db saat generate token --- app/Http/Controllers/Api/TokenController.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/Api/TokenController.php b/app/Http/Controllers/Api/TokenController.php index 73d8bfe0e3..260b930f18 100644 --- a/app/Http/Controllers/Api/TokenController.php +++ b/app/Http/Controllers/Api/TokenController.php @@ -32,7 +32,7 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; -use App\Models\SettingAplikasi; +use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Config; use Tymon\JWTAuth\Facades\JWTAuth; @@ -42,7 +42,7 @@ class TokenController extends Controller /** * Display a listing of the resource. * - * @return \Illuminate\Http\Response + * @return Response */ public function index() { @@ -51,12 +51,6 @@ public function index() $user = Auth::user(); $token = JWTAuth::fromUser($user); - // Simpan token ke das_setting agar middleware token.registered dapat memvalidasinya - SettingAplikasi::updateOrCreate( - ['key' => 'api_key_opendk'], - ['value' => $token] - ); - // Return the token in a response return response()->json(['token' => $token]); } From dcac218ced72a3a7a14116f031a076199e164b23 Mon Sep 17 00:00:00 2001 From: habibie11 Date: Thu, 25 Jun 2026 11:24:50 +0700 Subject: [PATCH 4/5] sesuaikan keamanan --- app/Http/Controllers/Api/TokenController.php | 31 +++++++++++++++----- app/Services/FileUploadService.php | 16 +++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/Api/TokenController.php b/app/Http/Controllers/Api/TokenController.php index 260b930f18..028f5515fc 100644 --- a/app/Http/Controllers/Api/TokenController.php +++ b/app/Http/Controllers/Api/TokenController.php @@ -34,24 +34,41 @@ use App\Http\Controllers\Controller; use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Config; +use Illuminate\Support\Facades\Log; use Tymon\JWTAuth\Facades\JWTAuth; class TokenController extends Controller { /** - * Display a listing of the resource. + * Generate token sinkronisasi dengan masa berlaku 1 tahun. + * + * Token ini digunakan untuk keperluan sinkronisasi data antar sistem, + * bukan untuk sesi autentikasi umum. * * @return Response */ public function index() { - // Set the token's expiration time, 10 tahun - Config::set('jwt.ttl', 10 * 365 * 24 * 60); $user = Auth::user(); - $token = JWTAuth::fromUser($user); - // Return the token in a response - return response()->json(['token' => $token]); + // Gunakan customClaims untuk set expiry 1 tahun secara terisolasi, + // tanpa mengubah konfigurasi JWT global via Config::set(). + // Ini memastikan TTL default untuk token lain tidak terpengaruh. + $expiresAt = now()->addYear()->timestamp; + + $token = JWTAuth::customClaims(['exp' => $expiresAt]) + ->fromUser($user); + + Log::info('Token sinkronisasi digenerate', [ + 'user_id' => $user->id, + 'expires_at' => now()->addYear()->toDateTimeString(), + 'ip' => request()->ip(), + ]); + + return response()->json([ + 'token' => $token, + 'expires_at' => now()->addYear()->toDateTimeString(), + 'token_type' => 'Bearer', + ]); } } diff --git a/app/Services/FileUploadService.php b/app/Services/FileUploadService.php index 6f2daca86d..c1b3bdf174 100644 --- a/app/Services/FileUploadService.php +++ b/app/Services/FileUploadService.php @@ -35,10 +35,18 @@ public function uploadSecure( // environment tertentu (seperti Laragon/Windows), $file->getRealPath() // me-return false untuk file di C:\Windows\Temp yang menyebabkan ValueError. $stream = fopen($file->getPathname(), 'r'); - $path = trim($directory.'/'.$safeFileName, '/'); - Storage::disk('public')->put($path, $stream); - if (is_resource($stream)) { - fclose($stream); + if ($stream === false) { + throw new \RuntimeException('Failed to open file: ' . $file->getPathname()); + } + + $path = trim($directory . '/' . $safeFileName, '/'); + + try { + Storage::disk('public')->put($path, $stream); + } finally { + if (is_resource($stream)) { + fclose($stream); + } } return $path; From 3e6cb5d0e381522c60aceaeb9fd81a9573a920b4 Mon Sep 17 00:00:00 2001 From: Ahmad Affandi Date: Fri, 26 Jun 2026 09:54:08 +0700 Subject: [PATCH 5/5] [ci skip] memutahirkan catatan rilis --- catatan_rilis.md | 1 + 1 file changed, 1 insertion(+) diff --git a/catatan_rilis.md b/catatan_rilis.md index 5a8709269b..6bf5167eff 100644 --- a/catatan_rilis.md +++ b/catatan_rilis.md @@ -14,6 +14,7 @@ Terimakasih [isi disini] yang telah berkontribusi langsung mengembangkan aplikas 4. [#1600](https://github.com/OpenSID/OpenDK/issues/1600) Perbaikan temuan pasca test manual 5. [#1601](https://github.com/OpenSID/OpenDK/issues/1601) Perbaikan Filter Kode Kecamatan pada Semua Halaman DataTable Gabungan 6. [#1605](https://github.com/OpenSID/OpenDK/issues/1605) Perbaikan Data dokumen yang ditambahkan/dihapus tidak tampil sesuai di halaman web +7. [#1608](https://github.com/OpenSID/OpenDK/issues/1608) Perbaikan generate token sinkronisasi opensid #### TEKNIS