fix: default permissions to true on API failure to prevent UI hiding
- When the permission check API call fails (timeout, network error, etc.), permissions now default to true instead of false - This prevents UI elements like the WiFi tab from disappearing when the permission check has a transient failure - The API still enforces access server-side, so no security impact - Added __checkFailed flag to PermissionMap for observability
This commit is contained in:
@@ -51,12 +51,14 @@ describe("permissions helpers", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("defaults requested permissions to false on API error", async () => {
|
it("defaults requested permissions to true on API error and marks __checkFailed", async () => {
|
||||||
mockCheckPermissions.mockRejectedValueOnce(new Error("request failed"));
|
mockCheckPermissions.mockRejectedValueOnce(new Error("request failed"));
|
||||||
|
|
||||||
const result = await checkPermissions("token", ["a", "b"]);
|
const result = await checkPermissions("token", ["a", "b"]);
|
||||||
|
|
||||||
expect(result).toEqual({ a: false, b: false });
|
expect(result.a).toBe(true);
|
||||||
|
expect(result.b).toBe(true);
|
||||||
|
expect(result.__checkFailed).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("hasPermission returns true only for explicit true values", () => {
|
it("hasPermission returns true only for explicit true values", () => {
|
||||||
|
|||||||
+13
-6
@@ -1,6 +1,9 @@
|
|||||||
import { optima } from "$lib";
|
import { optima } from "$lib";
|
||||||
|
|
||||||
export type PermissionMap = Record<string, boolean>;
|
export type PermissionMap = Record<string, boolean> & {
|
||||||
|
/** Set to `true` when the permission check itself failed (API error, timeout, etc.) */
|
||||||
|
__checkFailed?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check multiple permissions for the current user and return a map of
|
* Check multiple permissions for the current user and return a map of
|
||||||
@@ -34,11 +37,15 @@ export async function checkPermissions(
|
|||||||
}, {});
|
}, {});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Permission check failed:", err);
|
console.error("Permission check failed:", err);
|
||||||
// Default every requested permission to false on failure
|
// Default every requested permission to true on failure so the UI
|
||||||
return permissions.reduce<PermissionMap>((map, p) => {
|
// doesn't hide features that the user may actually be allowed to use.
|
||||||
map[p] = false;
|
// The API will still enforce access if the user truly lacks permission.
|
||||||
return map;
|
const map = permissions.reduce<PermissionMap>((m, p) => {
|
||||||
}, {});
|
m[p] = true;
|
||||||
|
return m;
|
||||||
|
}, {} as PermissionMap);
|
||||||
|
map.__checkFailed = true;
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,14 +85,19 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<form action="?/login" method="POST" onsubmit={handleSubmit} use:enhance={() => {
|
<form
|
||||||
// Called when the form action response comes back from the server
|
action="?/login"
|
||||||
return async ({ update }) => {
|
method="POST"
|
||||||
formActionDone = true;
|
onsubmit={handleSubmit}
|
||||||
$loading = false;
|
use:enhance={() => {
|
||||||
await update();
|
// Called when the form action response comes back from the server
|
||||||
};
|
return async ({ update }) => {
|
||||||
}}>
|
formActionDone = true;
|
||||||
|
$loading = false;
|
||||||
|
await update();
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
<input type="hidden" name="callbackKey" value={uriData.callbackKey} />
|
<input type="hidden" name="callbackKey" value={uriData.callbackKey} />
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
Reference in New Issue
Block a user