fix(settings): derive API-key scope groups and tool counts from the scope catalogue (#1924)
The API-key settings panel carried a hand-copied list of scope groups that
had drifted to 24 of the 30 scopes in API_KEY_SCOPES: articles:read/write,
companies:write and the three reconciliation scopes were missing, so a key
minted in the dashboard could not call gnubok_create_company, the article
tools, the seven reconciliation tools or the matching v1 endpoints. The
per-scope "N verktyg" counts in the panel and in the API_KEY_SCOPES
descriptions were hand-maintained and wrong (reports:read said 18, actual
30; bookkeeping:write said 11, actual 22).
- Move the pure scope catalogue (API_KEY_SCOPES, scope lists, SCOPE_GROUPS,
TOOL_SCOPE_MAP) into lib/auth/scope-catalog.ts with no server imports, so
the client-side panel can bundle it. api-keys.ts re-exports everything,
so existing imports are unchanged.
- SCOPE_GROUPS becomes a list of { domain, label, scopes } covering every
scope (reconciliation has three), shared by the panel and the OAuth
consent page. scopeKind() replaces the ad hoc suffix checks.
- TOOL_COUNT_BY_SCOPE is derived from TOOL_SCOPE_MAP at module load; the
hand-written counts are removed from the catalogue descriptions.
- The panel renders groups and cards from the catalogue; i18n keys are
derived from domain and scope id. The "(REST API)" heading suffix is
computed from the counts instead of baked into the labels.
- New unit test asserts every scope belongs to exactly one group and that
counts equal TOOL_SCOPE_MAP occurrences.
- New sv/en strings for the articles, companies:write and reconciliation
scopes.
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
f338850bd0
commit
b8605aabfc
@@ -13,6 +13,7 @@ import {
|
||||
API_KEY_SCOPES,
|
||||
DEFAULT_OAUTH_SCOPES,
|
||||
SCOPE_GROUPS,
|
||||
scopeKind,
|
||||
validateScopes,
|
||||
type ApiKeyScope,
|
||||
} from '@/lib/auth/api-keys'
|
||||
@@ -838,13 +839,10 @@ function renderScopeCheckboxes(
|
||||
|
||||
for (const group of SCOPE_GROUPS) {
|
||||
const rows: string[] = []
|
||||
if (group.read && ceiling.has(group.read)) {
|
||||
rows.push(scopeRow(group.read, preChecked.has(group.read), 'read'))
|
||||
renderedInGroups.add(group.read)
|
||||
}
|
||||
if (group.write && ceiling.has(group.write)) {
|
||||
rows.push(scopeRow(group.write, preChecked.has(group.write), 'write'))
|
||||
renderedInGroups.add(group.write)
|
||||
for (const scope of group.scopes) {
|
||||
if (!ceiling.has(scope)) continue
|
||||
rows.push(scopeRow(scope, preChecked.has(scope), scopeKind(scope)))
|
||||
renderedInGroups.add(scope)
|
||||
}
|
||||
if (rows.length > 0) {
|
||||
groups.push(
|
||||
@@ -853,11 +851,11 @@ function renderScopeCheckboxes(
|
||||
}
|
||||
}
|
||||
|
||||
// Defense in depth: the catalogue test guarantees full group coverage, so
|
||||
// this bucket is empty unless a scope ships without a group.
|
||||
const remaining = ALL_SCOPES.filter(s => ceiling.has(s) && !renderedInGroups.has(s))
|
||||
if (remaining.length > 0) {
|
||||
const rows = remaining.map((s) =>
|
||||
scopeRow(s, preChecked.has(s), s.endsWith(':write') || s.endsWith(':manage') || s.endsWith(':approve') ? 'write' : 'read')
|
||||
)
|
||||
const rows = remaining.map((s) => scopeRow(s, preChecked.has(s), scopeKind(s)))
|
||||
groups.push(
|
||||
`<div class="scope-group"><div class="scope-group-title">Övriga</div>${rows.join('')}</div>`
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user