6 Commits

Author SHA1 Message Date
putrakuningan
a1104e0c5a Change Authentication Background 2025-09-03 08:48:42 +07:00
Sholahuddin Al Ayubi
75701b53a9 Adds custom validation messages and improves error feedback
Introduces custom validation messages for login and password fields to enhance clarity for users. Updates error feedback text to provide more specific guidance when authentication fails. Fixes formatting issue in the password error message in the view for consistency.
2025-08-12 13:36:28 +07:00
Daeng Deni Mardaeni
0b377847cf feat(authentication): penyesuaian tampilan dan logika otentikasi pengguna
- Menambahkan `use Exception` pada kelas `Uim` untuk penanganan error yang lebih eksplisit.
- Menghapus penggunaan class `Request` yang tidak digunakan dalam `LoginRequest`.
- Memperbaiki logika penyesuaian role berdasarkan `KD_GROUP`, menetapkan `default` sebagai `customer_service` untuk menghindari kondisi default role tidak ditangani.
- Membuat beberapa perbaikan format dan konsistensi kelas serta elemen HTML di file blade:
  - Menyesuaikan atribut HTML untuk memastikan konsistensi seperti spasi, indentasi, dan tata letak elemen.
  - Memutakhirkan logo dan elemen visual untuk mendukung dinamika seperti nama aplikasi (`APP_NAME`) dan metode otentikasi (`METHOD_AUTH`) yang dapat dikonfigurasi melalui environment file.
  - Mengubah elemen teks dan gaya agar lebih responsif dan ramah pengguna.
- Meningkatkan pengalaman pengguna dengan memperbaiki hierarki elemen di form login dan halaman utama.

Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
2025-07-08 14:54:36 +07:00
Daeng Deni Mardaeni
cf5a7824c5 feat(authentication): perbaikan proses login dan penyesuaian role pengguna
- Memperbaiki proses login dengan menggunakan filter untuk email atau NIK sebagai parameter login.
- Menggunakan `updateOrCreate` untuk membuat atau memperbarui data user berdasarkan parameter login.
- Menambahkan logika untuk mendapatkan cabang berdasarkan 4 digit terakhir kode cabang (`KD_CABANG`) dan mengaitkannya dalam sesi serta data pengguna.
- Menerapkan penyesuaian role pengguna menggunakan kode grup pengguna (`KD_GROUP`) dengan logika `match`.
- Mengubah logika role menjadi lebih dinamis, memastikan pengguna mendapatkan role yang sesuai atau role default (`user`).
- Memastikan proses `session regenerate` terjadi setelah login.

Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
2025-06-22 16:59:57 +07:00
daengdeni
58e3339431 feat(authentication): integrasi sistem otentikasi dengan User Identity Management (UIM)
### Perubahan Utama
- Tambah integrasi dengan sistem UIM untuk otentikasi pengguna.
- Update logika otentikasi berdasarkan metode yang ditentukan (`METHOD_AUTH`).
- Penanganan otomatis terhadap pengguna baru dengan pembuatan akun dan penentuan role.

### Detail Perubahan
1. **Integrasi Sistem UIM**:
   - Tambah metode `userIdManagemeent` untuk menangani otentikasi pengguna melalui UIM.
   - Implementasikan fungsi `verify_user` di helper baru (`uim.php`) untuk berkomunikasi dengan server UIM.
   - Tambahkan class baru `Uim` untuk menangani verifikasi pengguna melalui koneksi TCP.
   - Dekode respons dari UIM untuk memproses data pengguna.

2. **Penyesuaian Logika Otentikasi**:
   - Jika `METHOD_AUTH` diset ke `uim`, sistem akan dialihkan ke otentikasi melalui UIM.
   - Tambahkan logika fallback untuk otentikasi standar menggunakan Laravel jika UIM tidak digunakan.

3. **Pembuatan Pengguna Baru**:
   - Buat pengguna dalam database secara otomatis jika belum terdaftar menggunakan informasi dari UIM.
   - Validasi kode cabang berdasarkan data UIM untuk menentukan cabang yang relevan.
   - Penetapan role pengguna sesuai dengan kode grup yang diterima dari UIM.

4. **Manajemen Cabang**:
   - Cari cabang terkait berdasarkan 4 digit terakhir kode cabang (`KD_CABANG`) dari data UIM.
   - Simpan ID cabang di session untuk pengguna yang berhasil otentikasi.

5. **Penanganan Validasi dan Error**:
   - Tambahkan mekanisme penanganan rate limit jika otentikasi gagal.
   - Beri pesan error yang lebih informatif saat otentikasi gagal melalui UIM.

6. **Update Konfigurasi Module**:
   - Tambahkan path baru untuk helper `uim.php` di `module.json`.
2025-06-20 14:01:02 +07:00
0b2335b237 Mengubah Halaman ketika sudah login ke halaman utama 2025-03-07 08:51:14 +07:00
6 changed files with 305 additions and 45 deletions

155
app/Classes/Uim.php Normal file
View File

@@ -0,0 +1,155 @@
<?php
namespace Modules\Authentication\Classes;
use Exception;
class Uim
{
private string $serviceHost;
private int $servicePort;
private string $appId;
private string $serverAddress;
/**
* @param string $serviceHost The host address of the user verification service
* @param int $servicePort The port of the user verification service
* @param string $appId The application ID
* @param string $serverAddress The server address
*/
public function __construct(string $serviceHost, int $servicePort, string $appId, string $serverAddress)
{
$this->serviceHost = $serviceHost;
$this->servicePort = $servicePort;
$this->appId = $appId;
$this->serverAddress = $serverAddress;
}
/**
* Verify a user's credentials
*
* @param string $userId The user ID
* @param string $password The user password
*
* @return string The verification response
* @throws Exception If connection fails or other errors occur
*/
public function verify(string $userId, string $password)
: string
{
$socket = $this->openSecureConnection();
$postData = $this->buildPostData($userId, $password);
$this->sendHttpRequest($socket, $postData);
$response = $this->readResponse($socket);
fclose($socket);
return $this->decodeHexResponse($response);
}
/**
* Open a secure connection to the verification service
*
* @return resource The socket connection
* @throws Exception If connection fails
*/
private function openSecureConnection()
{
$errno = 0;
$errstr = '';
$socket = @fsockopen("tcp://" . $this->serviceHost, $this->servicePort, $errno, $errstr, 30);
if (!$socket) {
throw new Exception("Connection failed: $errstr ($errno)");
}
return $socket;
}
/**
* Build the form data for the verification request
*
* @param string $userId The user ID
* @param string $password The user password
*
* @return string The URL-encoded form data
*/
private function buildPostData(string $userId, string $password)
: string
{
return http_build_query([
'appsid' => $this->appId,
'loginid' => $userId,
'passwd' => $password,
'addr' => $this->serverAddress,
'version' => 2
]);
}
/**
* Send the HTTP request to the verification service
*
* @param resource $socket The socket connection
* @param string $postData The data to send
*
* @return void
*/
private function sendHttpRequest($socket, string $postData)
: void
{
fwrite($socket, "POST /user_verification_dev.php HTTP/1.0\r\n");
fwrite($socket, "Host: {$this->serviceHost}\r\n");
fwrite($socket, "Content-type: application/x-www-form-urlencoded\r\n");
fwrite($socket, "Content-length: " . strlen($postData) . "\r\n");
fwrite($socket, "Accept: */*\r\n");
fwrite($socket, "\r\n");
fwrite($socket, "$postData\r\n");
fwrite($socket, "\r\n");
}
/**
* Read the response from the verification service
*
* @param resource $socket The socket connection
*
* @return string The response body
*/
private function readResponse($socket)
: string
{
// Skip headers
$headers = "";
while ($str = trim(fgets($socket, 4096))) {
$headers .= "$str\n";
}
// Read body
$body = "";
while (!feof($socket)) {
$body .= fgets($socket, 4096);
}
return $body;
}
/**
* Decode a hex-encoded response
*
* @param string $data The hex-encoded data
*
* @return string The decoded string
*/
private function decodeHexResponse(string $data)
: string
{
$text = '';
$total = strlen($data);
for ($j = 0; $j < $total; $j += 2) {
$text .= chr(hexdec(substr($data, $j, 2)));
}
return $text;
}
}

17
app/Helpers/uim.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
use Modules\Authentication\Classes\Uim;
if (!function_exists('verify_user')) {
function verify_user($id, $passwd, $SERVER_ADDR, $IPUserManager, $portUserManager, $appId)
{
try {
$verifier = new Uim($IPUserManager, $portUserManager, $appId, $SERVER_ADDR);
return $verifier->verify($id, $passwd);
} catch (Exception $e) {
// Handle error more gracefully than the original die()
error_log("User verification error: " . $e->getMessage());
return false;
}
}
}

View File

@@ -28,7 +28,8 @@ class AuthenticationController extends Controller
$request->session()->regenerate();
return redirect()->intended(('dashboard'));
// return redirect()->intended(('dashboard'));
return redirect('/');
}
/**

View File

@@ -8,6 +8,8 @@
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Modules\Basicdata\Models\Branch;
use Modules\Usermanagement\Models\User;
class LoginRequest extends FormRequest
{
@@ -25,6 +27,14 @@
];
}
public function messages()
{
return [
'login.required' => 'User tidak boleh kosong',
'password.required' => 'Password tidak boleh kosong',
];
}
/**
* Attempt to authenticate the request's credentials.
*
@@ -32,29 +42,96 @@
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate()
: void
public function authenticate(): void
{
$this->ensureIsNotRateLimited();
$credentials = $this->only('login', 'password');
$loginField = filter_var($credentials['login'], FILTER_VALIDATE_EMAIL) ? 'email' : 'nik';
$authData = [
$loginField => $credentials['login'],
'password' => $credentials['password'],
];
if ($_ENV['METHOD_AUTH'] == 'uim') {
$this->userIdManagemeent($credentials);
} else {
if (!Auth::attempt($authData, $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'login' => trans('auth.failed'),
'login' => 'Email/NIK atau password tidak sesuai.'
]);
}
RateLimiter::clear($this->throttleKey());
}
}
/**
* Authenticate user through user manager
*
* @param array $credentials
* @return \Illuminate\Http\RedirectResponse
*/
protected function userIdManagemeent($credentials)
{
$userArray = [];
$id = $credentials['login'];
$passwd = $credentials['password'];
$SERVER_ADDR = request()->ip();
$IPUserManager = $_ENV['IP_USER_MANAGER'];
$portUserManager = $_ENV['PORT_USER_MANAGER'];
$appId = $_ENV['APP_ID'];
$userData = verify_user($id, $passwd, $SERVER_ADDR, $IPUserManager, $portUserManager, $appId);
if (strlen($userData) > 1) {
$userRawArray = explode("\t", $userData);
foreach ($userRawArray as $rval) {
[$key, $val] = explode('=', $rval);
$userArray[0][$key] = $val;
}
// Use the login value to find the user
$loginField = filter_var($credentials['login'], FILTER_VALIDATE_EMAIL) ? 'email' : 'nik';
$kodeCabang = $userArray[0]['KD_CABANG']; // Example value containing the code
$lastFourDigits = substr($kodeCabang, -4); // Gets the last 4 characters
$branch = Branch::where('code', 'LIKE', '%' . $lastFourDigits)->first();
session()->put($userArray[0]);
session()->put('branch_id',$branch->id);
$user = User::updateOrCreate(
[$loginField => $credentials['login']],
[
'name' => $userArray[0]['NAMA_USER'],
'email' => $loginField === 'email' ? $credentials['login'] : null,
'nik' => $loginField === 'nik' ? $credentials['login'] : null,
'password' => bcrypt($credentials['password']),
'branch_id' => $branch ? $branch->id : null,
]
);
// Assign role based on user group code
$role = match($userArray[0]['KD_GROUP']) {
'001' => 'administrator',
default => 'customer_service'
};
$user->syncRoles($role);
Auth::loginUsingId($user->id, true);
$this->session()->regenerate();
RateLimiter::clear($this->throttleKey());
}
// Authentication failed
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'login' => 'Email/NIK atau password tidak sesuai.',
]);
}
/**
* Ensure the login request is not rate limited.

View File

@@ -8,6 +8,8 @@
"providers": [
"Modules\\Authentication\\Providers\\AuthenticationServiceProvider"
],
"files": [],
"files": [
"app/Helpers/uim.php"
],
"menu": {}
}

View File

@@ -3,50 +3,53 @@
@push('styles')
<style>
.branded-bg {
background-image:url('assets/media/images/2600x1600/1.png');
background-image: url('assets/media/images/2600x1600/bg-3.png');
}
.dark .branded-bg {
background-image: url('assets/media/images/2600x1600/1-dark.png');
background-image: url('assets/media/images/2600x1600/bg-3-dark.png');
}
</style>
@endpush
@section('content')
<div class="grid lg:grid-cols-2 grow">
<div class="flex justify-center items-center p-8 lg:p-10 order-2 lg:order-1">
<div class="flex order-2 justify-center items-center p-8 lg:p-10 lg:order-1">
<div class="card max-w-[370px] w-full">
<form action="{{ route('login') }}" class="card-body flex flex-col gap-5 p-10" id="sign_in_form" method="POST">
<form action="{{ route('login') }}" class="flex flex-col gap-5 p-10 card-body" id="sign_in_form" method="POST">
@csrf
<div class="text-center mb-2.5">
<h3 class="text-lg font-semibold text-gray-900 leading-none mb-2.5">
<div class="mb-2.5 text-center">
<h3 class="mb-2.5 text-lg font-semibold leading-none text-gray-900">
Sign in
</h3>
</div>
<div class="flex flex-col gap-1">
<label class="form-label text-gray-900">
<label class="text-gray-900 form-label">
Email or NIK
</label>
<input class="w-full input @error('login') border-danger @enderror" placeholder="Enter your email or NIK" type="text" name="login" value="{{ old('login') }}">
<input class="w-full input @error('login') border-danger @enderror"
placeholder="Enter your email or NIK" type="text" name="login" value="{{ old('login') }}">
@error('login')
<em class="alert text-danger text-sm">{{ $message }}</em>
<em class="text-sm alert text-danger">{{ $message }}</em>
@enderror
</div>
<div class="flex flex-col gap-1">
<div class="flex items-center justify-between gap-1">
<label class="form-label text-gray-900">
<div class="flex gap-1 justify-between items-center">
<label class="text-gray-900 form-label">
Password
</label>
</div>
<label class="input" data-toggle-password="true">
<input class="@error('password') border-danger @enderror" name="password" placeholder="Enter Password" type="password" value=""/>
<input class="@error('password') border-danger @enderror" name="password"
placeholder="Enter Password" type="password" value="" />
<div class="btn btn-icon" data-toggle-password-trigger="true">
<i class="ki-outline ki-eye toggle-password-active:hidden"></i>
<i class="ki-outline ki-eye-slash hidden toggle-password-active:block"></i>
<i class="hidden ki-outline ki-eye-slash toggle-password-active:block"></i>
</div>
@error('password')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</label>
@error('password')
<em class="text-sm alert text-danger">{{ $message }}</em>
@enderror
</div>
<label class="checkbox-group">
<input class="checkbox checkbox-sm" name="check" type="checkbox" value="1" />
@@ -54,29 +57,34 @@
Remember me
</span>
</label>
<button type="submit" class="btn btn-primary flex justify-center grow">
<button type="submit" class="flex justify-center btn btn-primary grow">
Sign In
</button>
</form>
</div>
</div>
<div class="lg:rounded-xl lg:border lg:border-gray-200 lg:m-5 order-1 lg:order-2 bg-top xxl:bg-center xl:bg-cover bg-no-repeat branded-bg">
<div class="flex flex-col p-8 lg:p-16 gap-4">
<a href="{{ route('dashboard') }}">
<img class="h-[100px] max-w-none" src="assets/media/app/logo-agi.png"/>
</a>
<div
class="order-1 bg-top bg-no-repeat lg:rounded-xl lg:border lg:border-gray-200 lg:m-5 lg:order-2 xxl:bg-center xl:bg-cover branded-bg">
<div class="flex flex-col gap-4 p-8 w-full lg:p-16">
<div class="flex w-full">
<img class="h-[100px] lg:h-[200px] max-w-none" src="assets/media/app/logo-agi.png" />
</div>
<div class="flex flex-col gap-3">
<h3 class="text-2xl font-semibold text-gray-900">
Secure Access Portal
<h3 class="text-4xl font-semibold text-gray-900">
{{ env('APP_NAME', 'Dashboard') }}
</h3>
<div class="text-base font-medium text-gray-600">
A robust authentication gateway ensuring
<div class="text-lg font-medium text-gray-600">
A robust authentication
@if (env('METHOD_AUTH') == 'uim')
integrate with <span class="font-semibold text-gray-900">User ID
Management</span>
@endif
gateway ensuring
<br />
secure
<span class="text-gray-900 font-semibold">
efficient user access
secure efficient user access to the
<span class="font-semibold text-gray-900">
{{ env('APP_NAME', 'Dashboard') }}
</span>
to the LPJ Online
<br />
Dashboard interface.
</div>