Add policy-based authz and token delegation
- Replace requireAdmin (role-based) guards on all REST endpoints
with RequirePolicy middleware backed by the existing policy engine;
built-in admin wildcard rule (-1) preserves existing admin behaviour
while operator rules can now grant targeted access to non-admin
accounts (e.g. a system account allowed to list accounts)
- Wire policy engine into Server: loaded from DB at startup,
reloaded after every policy-rule create/update/delete so changes
take effect immediately without a server restart
- Add service_account_delegates table (migration 000008) so a human
account can be delegated permission to issue tokens for a specific
system account without holding the admin role
- Add token-download nonce mechanism: a short-lived (5 min),
single-use random nonce is stored server-side after token issuance;
the browser downloads the token as a file via
GET /token/download/{nonce} (Content-Disposition: attachment)
instead of copying from a flash message
- Add /service-accounts UI page for non-admin delegates
- Add TestPolicyEnforcement and TestPolicyDenyRule integration tests
Security:
- Policy engine uses deny-wins, default-deny semantics; admin wildcard
is a compiled-in built-in and cannot be deleted via the API
- Token download nonces are 128-bit crypto/rand values, single-use,
and expire after 5 minutes; a background goroutine evicts stale entries
- alg header validation and Ed25519 signing unchanged
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
47
web/templates/fragments/token_delegates.html
Normal file
47
web/templates/fragments/token_delegates.html
Normal file
@@ -0,0 +1,47 @@
|
||||
{{define "token_delegates"}}
|
||||
<div id="token-delegates-section">
|
||||
<h3 style="font-size:.9rem;font-weight:600;margin-bottom:.5rem">Token Issue Delegates</h3>
|
||||
<p class="text-muted text-small" style="margin-bottom:.75rem">
|
||||
Delegates can issue and rotate tokens for this service account without holding the admin role.
|
||||
</p>
|
||||
{{if .TokenDelegates}}
|
||||
<table class="table table-sm" style="font-size:.85rem;margin-bottom:.75rem">
|
||||
<thead>
|
||||
<tr><th>Account</th><th>Granted</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .TokenDelegates}}
|
||||
<tr>
|
||||
<td>{{.GranteeName}}</td>
|
||||
<td class="text-small text-muted">{{formatTime .GrantedAt}}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
hx-delete="/accounts/{{$.Account.UUID}}/token/delegates/{{.GranteeUUID}}"
|
||||
hx-target="#token-delegates-section" hx-swap="outerHTML"
|
||||
hx-confirm="Remove delegate access for {{.GranteeName}}?">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<p class="text-muted text-small" style="margin-bottom:.75rem">No delegates.</p>
|
||||
{{end}}
|
||||
|
||||
{{if .DelegatableAccounts}}
|
||||
<form hx-post="/accounts/{{.Account.UUID}}/token/delegates"
|
||||
hx-target="#token-delegates-section" hx-swap="outerHTML"
|
||||
style="display:flex;gap:.5rem;align-items:center">
|
||||
<select class="form-control" name="grantee_uuid" required style="flex:1">
|
||||
<option value="">— select account to add as delegate —</option>
|
||||
{{range .DelegatableAccounts}}
|
||||
{{if eq (string .AccountType) "human"}}
|
||||
<option value="{{.UUID}}">{{.Username}}</option>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</select>
|
||||
<button class="btn btn-sm btn-secondary" type="submit">Add Delegate</button>
|
||||
</form>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -1,5 +1,16 @@
|
||||
{{define "token_list"}}
|
||||
<div id="token-list">
|
||||
{{if .Flash}}
|
||||
<div class="alert alert-success" role="alert" style="margin-bottom:1rem">
|
||||
{{.Flash}}
|
||||
{{if .DownloadNonce}}
|
||||
<div style="margin-top:.5rem">
|
||||
<a class="btn btn-sm btn-secondary"
|
||||
href="/token/download/{{.DownloadNonce}}">Download token as file</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Tokens}}
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
|
||||
Reference in New Issue
Block a user