GeoNetAgent, LDAPWeb, server-audit, server-connection
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

188 lines
12 KiB

@extends('layouts.app')
@section('title', 'LDAP Users — ' . config('app.name'))
@section('content')
<div class="max-w-[1600px] mx-auto px-4 py-8">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-8 mt-6">
<div>
<h1 class="text-3xl font-bold text-white">LDAP Users</h1>
<p class="text-slate-400 mt-1 text-sm">
Host: <span class="text-cyan-400">{{ $meta['server'] }}</span>
@if ($meta['domain'])
· Domain: <span class="text-cyan-400">{{ $meta['domain'] }}</span>
@endif
</p>
</div>
</div>
@include('partials.flash')
@if ($error)
<div class="mb-6 rounded-lg bg-red-950/50 border border-red-800 text-red-200 px-4 py-3">
{{ $error }}
</div>
@endif
<div class="flex flex-col xl:flex-row gap-6 items-start xl:items-stretch">
<div class="flex-1 min-w-0 bg-slate-900 border border-slate-800 rounded-2xl overflow-hidden w-full flex flex-col list-panel xl:sticky xl:top-4">
<div class="list-panel__toolbar p-4 border-b border-slate-800 flex flex-col md:flex-row gap-3 md:items-center md:justify-between bg-slate-900">
<form method="GET" class="flex flex-wrap gap-2 w-full md:w-auto">
<input type="hidden" name="sort" value="{{ $sort }}">
<input type="hidden" name="dir" value="{{ $dir }}">
<input name="q" value="{{ $search }}" placeholder="Search user, email, phone, note, or group..."
class="flex-1 min-w-[12rem] md:w-56 rounded-lg bg-slate-800 border border-slate-700 px-4 py-2 text-sm text-white">
<select name="group"
class="rounded-lg bg-slate-800 border border-slate-700 px-3 py-2 text-sm text-white min-w-[10rem]">
<option value="">All groups</option>
@foreach ($meta['all_groups'] ?? [] as $groupName)
<option value="{{ $groupName }}" @selected($groupFilter === $groupName)>{{ $groupName }}</option>
@endforeach
</select>
<button class="rounded-lg bg-cyan-600 hover:bg-cyan-500 px-4 py-2 text-sm font-medium">Filter</button>
@if ($search !== '' || $groupFilter !== '')
<a href="{{ route('ldap-users.index', ['sort' => $sort, 'dir' => $dir]) }}"
class="rounded-lg border border-slate-700 px-4 py-2 text-sm text-slate-300 hover:bg-slate-800">
Clear
</a>
@endif
</form>
<div class="text-sm text-slate-400">{{ $meta['total'] }} users</div>
</div>
<div class="list-panel__scroll">
<table class="min-w-full text-sm border-collapse list-table list-table--row-actions">
<thead class="text-slate-300">
<tr>
@php
$columns = [
'username' => 'Username',
'display_name' => 'Display Name',
'email' => 'Email',
'phone' => 'Phone',
'enabled' => 'Status',
'last_logon' => 'Last Logon',
'groups' => 'Groups',
];
@endphp
@foreach ($columns as $key => $label)
@php
$nextDir = ($sort === $key && $dir === 'asc') ? 'desc' : 'asc';
$arrow = $sort === $key ? ($dir === 'asc' ? '↑' : '↓') : '';
@endphp
<th class="sticky top-0 z-10 px-4 py-3 text-left font-medium whitespace-nowrap bg-slate-800 border-b border-slate-700">
<a href="?sort={{ $key }}&dir={{ $nextDir }}&q={{ urlencode($search) }}&group={{ urlencode($groupFilter) }}"
class="hover:text-cyan-400">{{ $label }} {{ $arrow }}</a>
</th>
@endforeach
<th class="sticky top-0 z-10 px-4 py-3 text-left font-medium whitespace-nowrap bg-slate-800 border-b border-slate-700">Meta</th>
<th class="list-col-actions sticky top-0 z-10 px-4 py-3 text-left font-medium whitespace-nowrap bg-slate-800 border-b border-slate-700">Actions</th>
@include('partials.list-table-row-hint', ['tag' => 'th'])
</tr>
</thead>
<tbody class="divide-y divide-slate-800">
@forelse ($users as $user)
<tr class="hover:bg-slate-800/40" data-list-row-title="{{ $user['username'] }}" data-list-row-subtitle="{{ $user['display_name'] }}">
<td class="px-4 py-3 font-medium text-white whitespace-nowrap">{{ $user['username'] }}</td>
<td class="px-4 py-3 text-slate-300 whitespace-nowrap">{{ $user['display_name'] }}</td>
<td class="px-4 py-3 text-slate-300 whitespace-nowrap">{{ $user['email'] }}</td>
<td class="px-4 py-3 text-slate-300 whitespace-nowrap">{{ $user['phone'] !== '' ? $user['phone'] : '—' }}</td>
<td class="px-4 py-3">
<span class="inline-flex rounded-full px-2 py-1 text-xs font-medium
{{ $user['enabled'] ? 'bg-emerald-950 text-emerald-300' : 'bg-amber-950 text-amber-300' }}">
{{ $user['enabled'] ? 'Enabled' : 'Disabled' }}
</span>
</td>
<td class="px-4 py-3 text-slate-300 whitespace-nowrap">{{ $user['last_logon'] ?? '—' }}</td>
<td class="px-4 py-3">
@if (count($user['groups']) === 0)
<span class="text-slate-500"></span>
@else
<div class="flex flex-wrap gap-1 max-w-md">
@foreach ($user['groups'] as $groupName)
<a href="{{ route('ldap-groups.show', $groupName) }}"
class="inline-flex rounded-md bg-slate-800 border border-slate-700 px-2 py-0.5 text-xs text-slate-300 hover:border-cyan-700 hover:text-cyan-300 whitespace-nowrap"
title="View group members">
{{ $groupName }}
</a>
@endforeach
</div>
@endif
</td>
<td class="px-4 py-3 whitespace-nowrap max-w-[10rem]">
@if (($user['other_email'] ?? '') !== '' || ($user['admin_note'] ?? '') !== '')
<div class="text-xs text-slate-400 truncate" title="{{ $user['admin_note'] ?? '' }}">
@if (($user['other_email'] ?? '') !== '')
<span class="text-slate-300">{{ $user['other_email'] }}</span>
@endif
@if (($user['admin_note'] ?? '') !== '')
<span class="block truncate">{{ Str::limit($user['admin_note'], 40) }}</span>
@endif
</div>
@else
<span class="text-slate-500"></span>
@endif
</td>
<td class="list-col-actions px-4 py-3 whitespace-nowrap">
<a href="{{ route('ldap-users.show', $user['username']) }}"
class="rounded bg-slate-700 hover:bg-slate-600 text-white text-xs px-2 py-1">View</a>
@if ($user['protected'])
<span class="text-xs text-slate-500 ml-1">Protected</span>
@else
<div class="flex flex-wrap gap-1 mt-1">
<a href="{{ route('ldap-users.edit', $user['username']) }}"
class="rounded bg-slate-700 hover:bg-slate-600 text-white text-xs px-2 py-1">Edit</a>
<form method="POST" action="{{ route('ldap-users.toggle-enabled', $user['username']) }}" class="inline"
onsubmit="return confirm('{{ $user['enabled'] ? 'Disable' : 'Enable' }} account [{{ $user['username'] }}]?');">
@csrf
<input type="hidden" name="confirm" value="1">
<button type="submit" class="rounded text-xs px-2 py-1 text-white
{{ $user['enabled'] ? 'bg-amber-700 hover:bg-amber-600' : 'bg-emerald-700 hover:bg-emerald-600' }}">
{{ $user['enabled'] ? 'Disable' : 'Enable' }}
</button>
</form>
<a href="{{ route('ldap-users.password', $user['username']) }}"
class="rounded bg-violet-700 hover:bg-violet-600 text-white text-xs px-2 py-1">Password</a>
</div>
@endif
</td>
@include('partials.list-table-row-hint', ['tag' => 'td'])
</tr>
@empty
<tr>
<td colspan="10" class="px-4 py-8 text-center text-slate-400">No users found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<aside class="w-full xl:w-80 shrink-0 bg-slate-900 border border-slate-800 rounded-2xl p-5 xl:sticky xl:top-4 notes-panel">
<h2 class="text-lg font-semibold text-white mb-4">Catatan</h2>
<dl class="space-y-4 text-sm">
<div>
<dt class="font-medium text-cyan-400 mb-1">Data aplikasi</dt>
<dd class="text-slate-400">Phone, other email, dan admin note disimpan di database (bukan LDAP).</dd>
</div>
<div>
<dt class="font-medium text-cyan-400 mb-1">Edit profil</dt>
<dd class="text-slate-400">Ubah display name dan email via LDAP modify.</dd>
</div>
<div>
<dt class="font-medium text-violet-400 mb-1">Reset password</dt>
<dd class="text-slate-400">Membutuhkan LDAPS (port 636). Konfirmasi ganda wajib.</dd>
</div>
<div>
<dt class="font-medium text-cyan-400 mb-1">Groups</dt>
<dd class="text-slate-400">Klik badge group untuk melihat anggota. Filter dropdown untuk analisa per group.</dd>
</div>
<div>
<dt class="font-medium text-amber-400 mb-1">Akun protected</dt>
<dd class="text-slate-400">Administrator, Guest, krbtgt tidak dapat dimodifikasi.</dd>
</div>
</dl>
</aside>
</div>
</div>
@endsection