Brings the ERP inventory export up to date with the platform backbone and field capture work. Conflicts in ARCHITECTURE.md, the API key service and the API key settings tab resolved by keeping both sides: - read and write keys stay separate; erp-inventory:read is read-only and cannot be combined with a write scope - the failed-attempt lockout counts in the shared ThrottlerHit table so it holds across API pods - the UI offers only scopes the user holds and creates one scope per key Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
43 lines
1.7 KiB
TypeScript
43 lines
1.7 KiB
TypeScript
import { PermissionKey } from '../auth/permissions/permission-catalog';
|
|
|
|
/**
|
|
* Permissions an API key may carry. Deliberately a short allowlist, not the
|
|
* whole catalog: a key is a long-lived secret stored in another system, so it
|
|
* only ever gets what a machine integration needs. shop-sync:write lets the
|
|
* shop book its own sales and returns - nothing else - and every booking is
|
|
* idempotent and kept in the ledger, so a leaked key cannot erase history.
|
|
* erp-inventory:read lets ERPNext read the inventory mirror (articles,
|
|
* batches with remaining stock, stock movements, harvest totals) - read
|
|
* only, no people, no notes. Removing an entry here also takes it away from
|
|
* existing keys (see ApiKeysService.authenticate).
|
|
*/
|
|
export const API_KEY_SCOPES = [
|
|
'shop-sync:read',
|
|
'shop-sync:write',
|
|
'erp-inventory:read',
|
|
] as const satisfies readonly PermissionKey[];
|
|
|
|
export type ApiKeyScope = (typeof API_KEY_SCOPES)[number];
|
|
|
|
/**
|
|
* Scopes that book (change data). Every other scope only reads. A new key
|
|
* never mixes both kinds: a leaked read key cannot book, a leaked booking
|
|
* key cannot read (ARCHITECTURE.md, Annahme 14).
|
|
*/
|
|
export const API_KEY_WRITE_SCOPES = [
|
|
'shop-sync:write',
|
|
] as const satisfies readonly ApiKeyScope[];
|
|
|
|
export function isApiKeyScope(value: string): value is ApiKeyScope {
|
|
return (API_KEY_SCOPES as readonly string[]).includes(value);
|
|
}
|
|
|
|
function isWriteScope(scope: string): boolean {
|
|
return (API_KEY_WRITE_SCOPES as readonly string[]).includes(scope);
|
|
}
|
|
|
|
/** True if the scopes hold at least one reading and one booking right. */
|
|
export function combinesReadAndWrite(scopes: readonly string[]): boolean {
|
|
return scopes.some(isWriteScope) && scopes.some((s) => !isWriteScope(s));
|
|
}
|