diff --git a/server-connection/geonet-console-deploy.tar.gz b/server-connection/geonet-console-deploy.tar.gz index 44a34fa..c4fb8c7 100644 Binary files a/server-connection/geonet-console-deploy.tar.gz and b/server-connection/geonet-console-deploy.tar.gz differ diff --git a/server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php b/server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php index 7b29997..e57e347 100644 --- a/server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php +++ b/server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php @@ -6,9 +6,14 @@ use App\Http\Controllers\Api\Concerns\RespondsWithJson; use App\Http\Controllers\Controller; use App\Services\LdapAuthService; use App\Services\LdapMetadataService; +use App\Services\LdapUserService; +use App\Services\MailDeliveryService; use App\Support\ApiActor; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use InvalidArgumentException; +use RuntimeException; +use Throwable; class MeController extends Controller { @@ -17,6 +22,8 @@ class MeController extends Controller public function __construct( private readonly LdapAuthService $ldapAuth, private readonly LdapMetadataService $metadata, + private readonly LdapUserService $ldapUsers, + private readonly MailDeliveryService $mailDelivery, ) { } @@ -27,7 +34,6 @@ class MeController extends Controller $data = $actor->toArray(); - // Untuk LDAP JWT — enrich dengan data profile dari LDAP + metadata DB if ($actor->type === 'ldap_jwt') { $profile = $this->ldapAuth->getProfile($actor->identifier); $meta = $this->metadata->getUserMeta($actor->identifier); @@ -42,4 +48,100 @@ class MeController extends Controller return $this->ok($data); } + + public function update(Request $request): JsonResponse + { + /** @var ApiActor $actor */ + $actor = $request->attributes->get('api_actor'); + + if ($actor->type !== 'ldap_jwt') { + return $this->fail('Hanya akun LDAP yang dapat mengubah profil.', 'forbidden', 403); + } + + $data = $request->validate([ + 'display_name' => ['required', 'string', 'max:255'], + 'email' => ['nullable', 'email', 'max:255'], + 'other_email' => ['nullable', 'email', 'max:255'], + 'phone' => ['nullable', 'string', 'max:32'], + ]); + + try { + $this->ldapUsers->updateProfile( + $actor->identifier, + $data['display_name'], + $data['email'] ?? null + ); + $this->metadata->saveUserSelfMeta( + $actor->identifier, + $data['other_email'] ?? null, + $data['phone'] ?? null + ); + } catch (InvalidArgumentException|RuntimeException $e) { + return $this->fail($e->getMessage(), 'unprocessable', 422); + } catch (Throwable) { + return $this->fail('Gagal memperbarui profil.', 'server_error', 500); + } + + return $this->ok(['message' => 'Profil berhasil diperbarui.']); + } + + public function changePassword(Request $request): JsonResponse + { + /** @var ApiActor $actor */ + $actor = $request->attributes->get('api_actor'); + + if ($actor->type !== 'ldap_jwt') { + return $this->fail('Hanya akun LDAP yang dapat mengubah password.', 'forbidden', 403); + } + + $data = $request->validate([ + 'current_password' => ['required', 'string'], + 'new_password' => ['required', 'string', 'min:8', 'confirmed'], + ]); + + $verified = $this->ldapAuth->attempt($actor->identifier, $data['current_password']); + if ($verified === null) { + return $this->fail('Password saat ini tidak sesuai.', 'invalid_credentials', 422); + } + + try { + $this->ldapUsers->resetPassword($actor->identifier, $data['new_password']); + } catch (InvalidArgumentException|RuntimeException $e) { + return $this->fail($e->getMessage(), 'unprocessable', 422); + } catch (Throwable) { + return $this->fail('Gagal mengubah password. Pastikan password memenuhi kebijakan AD.', 'server_error', 500); + } + + return $this->ok(['message' => 'Password berhasil diubah.']); + } + + public function testEmail(Request $request): JsonResponse + { + /** @var ApiActor $actor */ + $actor = $request->attributes->get('api_actor'); + + if ($actor->type !== 'ldap_jwt') { + return $this->fail('Hanya akun LDAP yang dapat mengirim test email.', 'forbidden', 403); + } + + $profile = $this->ldapAuth->getProfile($actor->identifier); + $meta = $this->metadata->getUserMeta($actor->identifier); + + $recipients = array_values(array_filter([ + ($profile['email'] ?? '') !== '' ? $profile['email'] : null, + ($meta['other_email'] ?? '') !== '' ? $meta['other_email'] : null, + ])); + + if ($recipients === []) { + return $this->fail('Tidak ada alamat email tersimpan. Tambahkan email di profil terlebih dahulu.', 'no_email', 422); + } + + try { + $sent = $this->mailDelivery->sendTestToMany($recipients); + } catch (Throwable) { + return $this->fail('Gagal mengirim email. Periksa konfigurasi SMTP.', 'smtp_error', 500); + } + + return $this->ok(['message' => 'Test email terkirim ke: ' . implode(', ', $sent) . '.', 'sent_to' => $sent]); + } } diff --git a/server-connection/geonet-console/routes/api.php b/server-connection/geonet-console/routes/api.php index 968fbf1..e74fe08 100644 --- a/server-connection/geonet-console/routes/api.php +++ b/server-connection/geonet-console/routes/api.php @@ -22,6 +22,9 @@ Route::prefix('v1')->group(function () { Route::middleware(['api.auth', 'api.audit'])->group(function () { Route::get('/me', [MeController::class, 'show'])->name('api.v1.me'); + Route::put('/me', [MeController::class, 'update'])->name('api.v1.me.update'); + Route::post('/me/password', [MeController::class, 'changePassword'])->name('api.v1.me.password'); + Route::post('/me/test-email', [MeController::class, 'testEmail'])->name('api.v1.me.test-email'); Route::get('/databases', [DatabaseController::class, 'index']) ->middleware('api.scope:databases:read')