UI: pgcreds create button; show logged-in user
* web/templates/pgcreds.html: New Credentials card is now always rendered; Add Credentials toggle button reveals the create form (hidden by default). Shows a message when all system accounts already have credentials. Previously the card was hidden when UncredentialedAccounts was empty. * internal/ui/ui.go: added ActorName string field to PageData; added actorName(r) helper resolving username from JWT claims via DB lookup, returns empty string if unauthenticated. * internal/ui/handlers_*.go: all full-page PageData constructors now pass ActorName: u.actorName(r). * web/templates/base.html: nav bar renders actor username as a muted label before the Logout button when logged in. * web/static/style.css: added .nav-actor rule (muted grey, 0.85rem).
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
<li><a href="/accounts">Accounts</a></li>
|
||||
<li><a href="/audit">Audit</a></li>
|
||||
<li><a href="/policies">Policies</a></li>
|
||||
<li><a href="/pgcreds">PG Creds</a></li>
|
||||
{{if .ActorName}}<li><span class="nav-actor">{{.ActorName}}</span></li>{{end}}
|
||||
<li><form method="POST" action="/logout" style="margin:0"><button class="btn btn-sm btn-secondary" type="submit">Logout</button></form></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -11,25 +11,84 @@
|
||||
{{else}}
|
||||
<p class="text-muted text-small" style="margin-bottom:1rem">No credentials stored.</p>
|
||||
{{end}}
|
||||
<form hx-put="/accounts/{{.Account.UUID}}/pgcreds"
|
||||
hx-target="#pgcreds-section" hx-swap="outerHTML">
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:.5rem">
|
||||
<input class="form-control" type="text" name="host" placeholder="Host" required
|
||||
value="{{if .PGCred}}{{.PGCred.PGHost}}{{end}}">
|
||||
<input class="form-control" type="number" name="port" placeholder="Port (5432)"
|
||||
min="1" max="65535"
|
||||
value="{{if .PGCred}}{{.PGCred.PGPort}}{{end}}">
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:.5rem">
|
||||
<input class="form-control" type="text" name="database" placeholder="Database" required
|
||||
value="{{if .PGCred}}{{.PGCred.PGDatabase}}{{end}}">
|
||||
<input class="form-control" type="text" name="username" placeholder="Username" required
|
||||
value="{{if .PGCred}}{{.PGCred.PGUsername}}{{end}}">
|
||||
</div>
|
||||
<input class="form-control" type="password" name="password"
|
||||
placeholder="Password (required to update)" required
|
||||
style="margin-bottom:.5rem">
|
||||
<button class="btn btn-sm btn-secondary" type="submit">Save Credentials</button>
|
||||
</form>
|
||||
|
||||
{{/* Any admin can add or update credentials; creator of the first set becomes owner */}}
|
||||
<details style="margin-bottom:1rem">
|
||||
<summary class="text-small" style="cursor:pointer;color:var(--color-text-muted)">
|
||||
{{if .PGCred}}Update credentials{{else}}Add new credentials{{end}}
|
||||
</summary>
|
||||
<form hx-put="/accounts/{{.Account.UUID}}/pgcreds"
|
||||
hx-target="#pgcreds-section" hx-swap="outerHTML"
|
||||
style="margin-top:.75rem">
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:.5rem">
|
||||
<input class="form-control" type="text" name="host" placeholder="Host" required
|
||||
{{if .PGCred}}value="{{.PGCred.PGHost}}"{{end}}>
|
||||
<input class="form-control" type="number" name="port" placeholder="Port (5432)"
|
||||
min="1" max="65535"
|
||||
{{if .PGCred}}value="{{.PGCred.PGPort}}"{{end}}>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:.5rem">
|
||||
<input class="form-control" type="text" name="database" placeholder="Database" required
|
||||
{{if .PGCred}}value="{{.PGCred.PGDatabase}}"{{end}}>
|
||||
<input class="form-control" type="text" name="username" placeholder="Username" required
|
||||
{{if .PGCred}}value="{{.PGCred.PGUsername}}"{{end}}>
|
||||
</div>
|
||||
<input class="form-control" type="password" name="password"
|
||||
placeholder="Password (required)" required
|
||||
style="margin-bottom:.5rem">
|
||||
<button class="btn btn-sm btn-secondary" type="submit">Save Credentials</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
{{/* Access grants section — shown whenever credentials exist */}}
|
||||
{{if .PGCred}}
|
||||
<div style="margin-top:1.25rem">
|
||||
<h3 style="font-size:.9rem;font-weight:600;margin-bottom:.5rem">Access Grants</h3>
|
||||
{{if .PGCredGrants}}
|
||||
<table class="table table-sm" style="font-size:.85rem">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Granted</th>
|
||||
{{if isPGCredOwner $.ActorID $.PGCred}}<th></th>{{end}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .PGCredGrants}}
|
||||
<tr>
|
||||
<td>{{.GranteeName}}</td>
|
||||
<td class="text-small text-muted">{{formatTime .GrantedAt}}</td>
|
||||
{{if isPGCredOwner $.ActorID $.PGCred}}
|
||||
<td>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
hx-delete="/accounts/{{$.Account.UUID}}/pgcreds/access/{{.GranteeUUID}}"
|
||||
hx-target="#pgcreds-section" hx-swap="outerHTML"
|
||||
hx-confirm="Revoke access for {{.GranteeName}}?">Revoke</button>
|
||||
</td>
|
||||
{{end}}
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<p class="text-muted text-small">No access grants.</p>
|
||||
{{end}}
|
||||
|
||||
{{/* Grant form — owner only */}}
|
||||
{{if and (isPGCredOwner .ActorID .PGCred) .GrantableAccounts}}
|
||||
<form hx-post="/accounts/{{.Account.UUID}}/pgcreds/access"
|
||||
hx-target="#pgcreds-section" hx-swap="outerHTML"
|
||||
style="margin-top:.75rem;display:flex;gap:.5rem;align-items:center">
|
||||
<select class="form-control" name="grantee_uuid" required style="flex:1">
|
||||
<option value="">— select account to grant —</option>
|
||||
{{range .GrantableAccounts}}
|
||||
<option value="{{.UUID}}">{{.Username}} ({{.AccountType}})</option>
|
||||
{{end}}
|
||||
</select>
|
||||
<button class="btn btn-sm btn-secondary" type="submit">Grant Access</button>
|
||||
</form>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
134
web/templates/pgcreds.html
Normal file
134
web/templates/pgcreds.html
Normal file
@@ -0,0 +1,134 @@
|
||||
{{define "pgcreds"}}{{template "base" .}}{{end}}
|
||||
{{define "title"}}PG Credentials — MCIAS{{end}}
|
||||
{{define "content"}}
|
||||
<div class="page-header">
|
||||
<h1>Postgres Credentials</h1>
|
||||
<p class="text-muted text-small">Credentials you own or have been granted access to.</p>
|
||||
</div>
|
||||
|
||||
{{if .Creds}}
|
||||
<div class="card">
|
||||
<h2 style="font-size:1rem;font-weight:600;margin-bottom:1rem">Your Credentials</h2>
|
||||
{{range .Creds}}
|
||||
<div style="border:1px solid var(--color-border);border-radius:6px;padding:1rem;margin-bottom:1rem">
|
||||
<dl style="display:grid;grid-template-columns:140px 1fr;gap:.35rem .75rem;font-size:.9rem;margin-bottom:.75rem">
|
||||
<dt class="text-muted">Service Account</dt><dd>{{.ServiceUsername}}</dd>
|
||||
<dt class="text-muted">Host</dt><dd>{{.PGHost}}:{{.PGPort}}</dd>
|
||||
<dt class="text-muted">Database</dt><dd>{{.PGDatabase}}</dd>
|
||||
<dt class="text-muted">Username</dt><dd>{{.PGUsername}}</dd>
|
||||
<dt class="text-muted">Updated</dt><dd class="text-small text-muted">{{formatTime .UpdatedAt}}</dd>
|
||||
</dl>
|
||||
|
||||
{{/* Grant management — only for the credential owner */}}
|
||||
{{$credID := .ID}}
|
||||
{{$svcUUID := .ServiceAccountUUID}}
|
||||
{{$grants := index $.CredGrants $credID}}
|
||||
{{if isPGCredOwner $.ActorID .}}
|
||||
<div style="margin-top:.75rem">
|
||||
<h3 style="font-size:.85rem;font-weight:600;margin-bottom:.5rem">Access Grants</h3>
|
||||
{{if $grants}}
|
||||
<table class="table table-sm" style="font-size:.85rem;margin-bottom:.75rem">
|
||||
<thead>
|
||||
<tr><th>User</th><th>Granted</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $grants}}
|
||||
<tr>
|
||||
<td>{{.GranteeName}}</td>
|
||||
<td class="text-small text-muted">{{formatTime .GrantedAt}}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
hx-delete="/accounts/{{$svcUUID}}/pgcreds/access/{{.GranteeUUID}}?_next=/pgcreds"
|
||||
hx-confirm="Revoke access for {{.GranteeName}}?">Revoke</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<p class="text-muted text-small" style="margin-bottom:.5rem">No access grants.</p>
|
||||
{{end}}
|
||||
|
||||
{{/* Plain POST grant form with redirect back to /pgcreds */}}
|
||||
<form method="POST" action="/accounts/{{.ServiceAccountUUID}}/pgcreds/access"
|
||||
style="display:flex;gap:.5rem;align-items:center">
|
||||
<input type="hidden" name="_csrf" value="{{$.CSRFToken}}">
|
||||
<input type="hidden" name="_next" value="/pgcreds">
|
||||
<select class="form-control" name="grantee_uuid" required style="flex:1">
|
||||
<option value="">— select account to grant —</option>
|
||||
{{range $.AllAccounts}}
|
||||
<option value="{{.UUID}}">{{.Username}} ({{.AccountType}})</option>
|
||||
{{end}}
|
||||
</select>
|
||||
<button class="btn btn-sm btn-secondary" type="submit">Grant Access</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div style="margin-top:.75rem">
|
||||
<a class="btn btn-sm btn-secondary" href="/accounts/{{.ServiceAccountUUID}}">View Account</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="card">
|
||||
<p class="text-muted">No Postgres credentials are accessible to your account.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="card" style="margin-top:1.5rem">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem">
|
||||
<h2 style="font-size:1rem;font-weight:600;margin:0">New Credentials</h2>
|
||||
<button class="btn btn-sm btn-secondary"
|
||||
onclick="var f=document.getElementById('pgcreds-create-form');f.hidden=!f.hidden;this.textContent=f.hidden?'Add Credentials':'Cancel'">Add Credentials</button>
|
||||
</div>
|
||||
<div id="pgcreds-create-form" hidden>
|
||||
<p class="text-muted text-small" style="margin-bottom:1rem;margin-top:.5rem">
|
||||
Create a credential set for a system account. You will become the owner and
|
||||
may later grant other accounts read access.
|
||||
</p>
|
||||
{{if .UncredentialedAccounts}}
|
||||
<form method="POST" action="/pgcreds">
|
||||
<input type="hidden" name="_csrf" value="{{.CSRFToken}}">
|
||||
<div style="margin-bottom:.75rem">
|
||||
<label class="form-label" for="pgcreds-account">Service Account</label>
|
||||
<select id="pgcreds-account" class="form-control" name="account_uuid" required>
|
||||
<option value="">— select system account —</option>
|
||||
{{range .UncredentialedAccounts}}
|
||||
<option value="{{.UUID}}">{{.Username}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:.5rem">
|
||||
<div>
|
||||
<label class="form-label" for="pgcreds-host">Host</label>
|
||||
<input id="pgcreds-host" class="form-control" type="text" name="host" placeholder="db.example.com" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label" for="pgcreds-port">Port</label>
|
||||
<input id="pgcreds-port" class="form-control" type="number" name="port" placeholder="5432" min="1" max="65535">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:.5rem">
|
||||
<div>
|
||||
<label class="form-label" for="pgcreds-db">Database</label>
|
||||
<input id="pgcreds-db" class="form-control" type="text" name="database" placeholder="myapp" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label" for="pgcreds-user">Username</label>
|
||||
<input id="pgcreds-user" class="form-control" type="text" name="username" placeholder="svc_user" required>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom:.75rem">
|
||||
<label class="form-label" for="pgcreds-password">Password</label>
|
||||
<input id="pgcreds-password" class="form-control" type="password" name="password" required>
|
||||
</div>
|
||||
<button class="btn btn-secondary" type="submit">Create Credentials</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<p class="text-muted text-small">All system accounts already have credentials. Create a new system account first.</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user