Add RBAC support with roles, permissions, and session management. Implement middleware for authentication and CSRF protection. Enhance audit logging with additional fields. Update HTTP handlers and routes for new features.

This commit is contained in:
2025-12-13 17:44:09 +00:00
parent d69e01bbaf
commit 8100f87686
44 changed files with 3262 additions and 76 deletions

View File

@@ -5,9 +5,20 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/htmx.org@1.9.2"></script>
<script>
// HTMX CSRF token support
document.body.addEventListener('htmx:configRequest', function(event) {
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
if (csrfToken) {
event.detail.headers['X-CSRF-Token'] = csrfToken;
}
});
</script>
<title>{{.Title}}</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<meta name="csrf-token" content="fake-csrf-token">
{{if .CSRFToken}}
<meta name="csrf-token" content="{{.CSRFToken}}">
{{end}}
</head>
<body class="bg-gray-100">
<main class="container mx-auto p-4">

View File

@@ -0,0 +1,32 @@
{{ define "hx_iscsi_luns" }}
<div>
<table class="w-full">
<thead><tr><th>LUN ID</th><th>ZVol</th><th>Size</th><th>Action</th></tr></thead>
<tbody>
{{ if . }}
{{ range . }}
<tr>
<td>{{ .lun_id }}</td>
<td>{{ .zvol }}</td>
<td>{{ .size }}</td>
<td>
<div>
<form hx-post="/api/iscsi/unmap_lun" hx-include="closest form" style="display:inline-block">
<input type="hidden" name="id" value="{{ .id }}" />
<button type="submit">Drain</button>
</form>
<form hx-post="/api/iscsi/delete_lun" hx-include="closest form" style="display:inline-block">
<input type="hidden" name="id" value="{{ .id }}" />
<input type="checkbox" name="force" id="force-{{ .id }}" value="1" />
<label for="force-{{ .id }}">Force delete</label>
<button type="submit">Delete</button>
</form>
</div>
</td>
</tr>
{{ end }}
{{ end }}
</tbody>
</table>
</div>
{{ end }}

View File

@@ -0,0 +1,18 @@
{{ define "hx_iscsi_target_info" }}
<div>
<h4>Initiator Connection</h4>
<p>IQN: {{ .iqn }}</p>
<h5>Portals</h5>
<ul>
{{ range .portals }}
<li>{{ .address }}:{{ .port }}</li>
{{ end }}
</ul>
<h5>Allowed Initiators</h5>
<ul>
{{ range .initiators }}
<li>{{ .iqn }}</li>
{{ end }}
</ul>
</div>
{{ end }}

View File

@@ -0,0 +1,19 @@
{{ define "hx_iscsi_targets" }}
<table class="w-full">
<thead><tr><th>Name</th><th>IQN</th><th>Action</th></tr></thead>
<tbody>
{{ if . }}
{{ range . }}
<tr>
<td>{{ .name }}</td>
<td>{{ .iqn }}</td>
<td>
<button hx-get="/api/iscsi/hx_luns/{{ .id }}">View LUNs</button>
<button hx-get="/api/iscsi/target/{{ .id }}">Connection Info</button>
</td>
</tr>
{{ end }}
{{ end }}
</tbody>
</table>
{{ end }}

View File

@@ -0,0 +1,23 @@
{{define "hx_nfs_shares"}}
<div>
<table class="min-w-full bg-white">
<thead>
<tr><th>Name</th><th>Path</th><th>Type</th><th></th></tr>
</thead>
<tbody>
{{range .}}
<tr class="border-t"><td>{{.Name}}</td><td>{{.Path}}</td><td>{{.Type}}</td>
<td>
<form hx-post="/shares/nfs/delete" hx-swap="outerHTML" class="inline">
<input type="hidden" name="id" value="{{.ID}}" />
<button class="px-2 py-1 bg-red-500 text-white rounded text-xs">Delete</button>
</form>
</td>
</tr>
{{else}}
<tr><td colspan="4">No NFS shares</td></tr>
{{end}}
</tbody>
</table>
</div>
{{end}}

View File

@@ -0,0 +1,23 @@
{{define "hx_smb_shares"}}
<div>
<table class="min-w-full bg-white">
<thead>
<tr><th>Name</th><th>Path</th><th>Type</th><th>Options</th><th></th></tr>
</thead>
<tbody>
{{range .}}
<tr class="border-t"><td>{{.Name}}</td><td>{{.Path}}</td><td>{{.Type}}</td><td>{{range $k,$v := .Config}}{{$k}}={{$v}} {{end}}</td>
<td>
<form hx-post="/shares/smb/delete" hx-swap="outerHTML" class="inline">
<input type="hidden" name="id" value="{{.ID}}" />
<button class="px-2 py-1 bg-red-500 text-white rounded text-xs">Delete</button>
</form>
</td>
</tr>
{{else}}
<tr><td colspan="5">No SMB shares</td></tr>
{{end}}
</tbody>
</table>
</div>
{{end}}

View File

@@ -0,0 +1,23 @@
{{ define "iscsi" }}
<div class="p-4">
<h2 class="text-xl">iSCSI Targets</h2>
<div hx-get="/api/iscsi/hx_targets" hx-swap="outerHTML"></div>
<div class="mt-4">
<h3>Create Target</h3>
<form hx-post="/api/iscsi/create_target">
<label>Name: <input type="text" name="name"/></label>
<label>IQN: <input type="text" name="iqn"/></label>
<button type="submit">Create Target</button>
</form>
</div>
<div class="mt-4">
<h3>Create LUN</h3>
<form hx-post="/api/iscsi/create_lun">
<label>Target ID: <input type="text" name="target_id"/></label>
<label>ZVol path: <input type="text" name="zvol"/></label>
<label>Size (e.g. 10G): <input type="text" name="size"/></label>
<button type="submit">Create LUN</button>
</form>
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,22 @@
{{define "content"}}
<div class="bg-white rounded shadow p-4">
<h1 class="text-2xl font-bold">NFS Shares</h1>
<div class="mt-4">
<button class="px-3 py-2 bg-blue-500 text-white rounded" hx-get="/hx/shares/nfs" hx-swap="outerHTML" hx-target="#nfs-shares">Refresh</button>
</div>
<div id="nfs-shares" class="mt-4">
{{template "hx_nfs_shares" .}}
</div>
<div class="mt-6">
<h2 class="text-lg font-semibold">Create NFS Share</h2>
<form hx-post="/shares/nfs/create" hx-swap="afterbegin" class="mt-2">
<div class="flex space-x-2">
<input name="name" placeholder="share name" class="border rounded p-1" />
<input name="path" placeholder="dataset (e.g. tank/ds)" class="border rounded p-1" />
<input name="options" placeholder='{"clients":"*(rw)"}' class="border rounded p-1 w-64" />
<button class="px-3 py-1 bg-green-500 text-white rounded" type="submit">Create</button>
</div>
</form>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,23 @@
{{define "content"}}
<div class="bg-white rounded shadow p-4">
<h1 class="text-2xl font-bold">SMB Shares</h1>
<div class="mt-4">
<button class="px-3 py-2 bg-blue-500 text-white rounded" hx-get="/hx/shares/smb" hx-swap="outerHTML" hx-target="#smb-shares">Refresh</button>
</div>
<div id="smb-shares" class="mt-4">
{{template "hx_smb_shares" .}}
</div>
<div class="mt-6">
<h2 class="text-lg font-semibold">Create SMB Share</h2>
<form hx-post="/shares/smb/create" hx-swap="afterbegin" class="mt-2">
<div class="flex space-x-2">
<input name="name" placeholder="share name" class="border rounded p-1" />
<input name="path" placeholder="dataset (e.g. tank/ds)" class="border rounded p-1" />
<input name="allowed_users" placeholder="user1,user2" class="border rounded p-1" />
<label class="text-sm">Read only <input type="checkbox" name="read_only" value="1" /></label>
<button class="px-3 py-1 bg-green-500 text-white rounded" type="submit">Create</button>
</div>
</form>
</div>
</div>
{{end}}

View File

@@ -5,7 +5,7 @@
<button class="px-3 py-2 bg-blue-500 text-white rounded" hx-get="/hx/pools" hx-swap="outerHTML" hx-target="#pools">Refresh pools</button>
</div>
<div id="pools" class="mt-4">
{{template "hx_pools.html" .}}
{{template "hx_pools" .}}
</div>
<div class="mt-6">
<h2 class="text-lg font-semibold">Create Pool</h2>