mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-21 23:56:39 +00:00
fix(nodes): never send stored node tokens to clients (#1281)
Node read endpoints now return a client-safe projection that omits the stored api_token and exposes a has_token boolean instead, so a node's long-lived proxy credential is never serialized to a browser or API token client. The token stays encrypted at rest and is read server-side only by the components that need it (the remote proxy, the connection test, and the mesh dialer). The edit form opens the API Token field blank, and a blank value keeps the existing credential, so saving an edit without retyping the token no longer clears it; a non-empty value rotates it. The backend enforces the same rule defensively. Node management actions (add, edit, delete, test connection, generate node token) are gated in the UI to match their server-side permission checks, so operators no longer see an action the API would reject. The test-connection route also gains the missing server-side permission and token-scope guards. Also validate the x-node-id header and fall back to the default node for malformed values instead of an obscure 404, and return 400 (not 500) when deleting the default node.
This commit is contained in:
@@ -38,8 +38,8 @@ export function NodeManager() {
|
||||
const { isAdmin, can } = useAuth();
|
||||
const canEditLabels = isPaid && isAdmin;
|
||||
// Mirror the backend node:manage guard. This top-level flag checks the global
|
||||
// role only (admin or global node-admin); the per-row Edit/Delete buttons below
|
||||
// additionally honor scoped per-node grants via can('node:manage', 'node', id).
|
||||
// role only (admin or global node-admin); the per-row Test/Edit/Delete buttons
|
||||
// below additionally honor scoped per-node grants via can('node:manage', 'node', id).
|
||||
// Admins resolve immediately via isAdmin; node-admins once /permissions/me lands.
|
||||
// Generate-token and reset-anchor below stay admin-only to match their stricter
|
||||
// backend guards (requireAdmin, and requireAdmin + requirePaid).
|
||||
@@ -453,23 +453,25 @@ export function NodeManager() {
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => testConnection(node)}
|
||||
disabled={testing === node.id}
|
||||
aria-label="Test connection"
|
||||
>
|
||||
<Wifi className={`w-4 h-4 ${testing === node.id ? 'animate-pulse' : ''}`} />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Test Connection</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
{canManageThis && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => testConnection(node)}
|
||||
disabled={testing === node.id}
|
||||
aria-label="Test connection"
|
||||
>
|
||||
<Wifi className={`w-4 h-4 ${testing === node.id ? 'animate-pulse' : ''}`} />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Test Connection</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
{canManageThis && (
|
||||
<TooltipProvider>
|
||||
|
||||
Reference in New Issue
Block a user