mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 09:43:12 +00:00
fix(work-orders): trust only JWT org_scope for account list
Ignore stored AuthUser.orgScope so a scoped token cannot open the unscoped account picker via localStorage.
This commit is contained in:
parent
2bff57fd02
commit
4f16b6a76f
4 changed files with 24 additions and 13 deletions
|
|
@ -7,7 +7,7 @@ export interface AuthUser {
|
|||
fullname: string;
|
||||
id: string | number;
|
||||
name?: string;
|
||||
/** JWT / login `org_scope`. Only `all` may see org-wide account lists. */
|
||||
/** Login payload field. Not trusted for org-wide UI gates; use the JWT claim. */
|
||||
orgScope?: string;
|
||||
accountId?: string | number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,13 +33,12 @@ function readStringClaim(source: Record<string, unknown> | null, keys: string[])
|
|||
return "";
|
||||
}
|
||||
|
||||
/** Org-wide only when the authenticated scope is explicitly `all`. Fail-closed otherwise. */
|
||||
/** Org-wide only when the JWT claim is explicitly `all`. Fail-closed otherwise. */
|
||||
export function isOrgWideScope(user: AuthUser | null | undefined): boolean {
|
||||
if (!user) {
|
||||
if (!user || typeof user.token !== "string") {
|
||||
return false;
|
||||
}
|
||||
const claims = typeof user.token === "string" ? readJwtClaims(user.token) : null;
|
||||
const fromJwt = readStringClaim(claims, ["org_scope", "orgScope", "OrgScope"]);
|
||||
const fromUser = typeof user.orgScope === "string" ? user.orgScope.trim() : "";
|
||||
return (fromJwt || fromUser).toLowerCase() === ORG_SCOPE_ALL;
|
||||
const claims = readJwtClaims(user.token);
|
||||
const scope = readStringClaim(claims, ["org_scope", "orgScope", "OrgScope"]);
|
||||
return scope.toLowerCase() === ORG_SCOPE_ALL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ function encodeJwt(payload: Record<string, unknown>): string {
|
|||
return `eyJhbGciOiJub25lIn0.${b64}.sig`;
|
||||
}
|
||||
|
||||
function authUser(token: string): AuthUser {
|
||||
function authUser(token: string, orgScope?: string): AuthUser {
|
||||
return {
|
||||
token,
|
||||
expiration: new Date(Date.now() + 60_000).toISOString(),
|
||||
|
|
@ -59,6 +59,7 @@ function authUser(token: string): AuthUser {
|
|||
phoneNumber: "",
|
||||
fullname: "Test User",
|
||||
id: 1,
|
||||
orgScope,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -132,4 +133,12 @@ describe("WizardStepLocationAccountSelect", () => {
|
|||
expect(screen.queryByText(FOREIGN_ACCOUNT)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Amazon")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps the list disabled when a scoped token has no org_scope and storage says all", () => {
|
||||
renderSelect(authUser(encodeJwt({ account_id: 7 }), "all"));
|
||||
|
||||
expect(useAccountsListMock).toHaveBeenCalledWith({ page: 1, pageSize: 50, search: "" }, false);
|
||||
expect(screen.queryByRole("button", { name: /select account/i })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(FOREIGN_ACCOUNT)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,16 +36,19 @@ describe("isOrgWideScope", () => {
|
|||
expect(isOrgWideScope(user(encodeJwt({ org_scope: "account", account_id: 7 })))).toBe(false);
|
||||
});
|
||||
|
||||
it("uses the login orgScope when the token is not a JWT", () => {
|
||||
expect(isOrgWideScope(user("session-token", "all"))).toBe(true);
|
||||
expect(isOrgWideScope(user("session-token", "account"))).toBe(false);
|
||||
it("ignores a stored orgScope when the token is not a JWT", () => {
|
||||
expect(isOrgWideScope(user("session-token", "all"))).toBe(false);
|
||||
});
|
||||
|
||||
it("prefers a scoped JWT over a stale org-wide login field", () => {
|
||||
it("ignores a stale stored orgScope when the JWT has no org_scope", () => {
|
||||
expect(isOrgWideScope(user(encodeJwt({ account_id: 7 }), "all"))).toBe(false);
|
||||
});
|
||||
|
||||
it("ignores a stale stored orgScope when the JWT is scoped", () => {
|
||||
expect(isOrgWideScope(user(encodeJwt({ org_scope: "account" }), "all"))).toBe(false);
|
||||
});
|
||||
|
||||
it("is false when neither the JWT nor the user declares all", () => {
|
||||
it("is false when the token has no org_scope claim", () => {
|
||||
expect(isOrgWideScope(user("wo-visual-token"))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue