fix(api-keys): normalize legacy scope metadata

This commit is contained in:
saltbo
2026-07-23 01:50:22 -04:00
parent 387c731b3b
commit a83c3ac5a7
4 changed files with 41 additions and 14 deletions
+1 -1
View File
@@ -622,7 +622,7 @@ func (c *Client) createMatter(
onConflict := openapi.CreateObjectJSONBodyOnConflictRename
res, err := c.api.CreateObjectWithResponse(ctx, openapi.CreateObjectJSONRequestBody{
Name: name,
Type: contentType,
Type: &contentType,
Size: &sizeInt,
Parent: &parent,
Dirtype: &dirtype,
+1 -1
View File
@@ -3708,7 +3708,7 @@ type CreateObjectJSONBody struct {
Parent *string `json:"parent,omitempty"`
Size *int `json:"size,omitempty"`
StorageId *string `json:"storageId,omitempty"`
Type string `json:"type"`
Type *string `json:"type,omitempty"`
}
// CreateObjectJSONBodyOnConflict defines parameters for CreateObject.
+18 -9
View File
@@ -1,5 +1,5 @@
import { type ApiKeyScope, ApiKeyTemplate, apiKeyMetadata, parseApiKeyScope } from '@shared/api-key-templates'
import { and, eq, inArray, isNull } from 'drizzle-orm'
import { and, eq, inArray } from 'drizzle-orm'
import { apikey, member } from '../../db/auth-schema'
import type { Database } from '../../platform/interface'
import { resolveOrganizationOwnerUserId } from './organization-owner'
@@ -56,13 +56,22 @@ export async function normalizeLegacyApiKey(
}
export async function normalizeLegacyApiKeysForUser(db: Database, userId: string): Promise<void> {
await db
.update(apikey)
.set({
metadata: JSON.stringify(apiKeyMetadata({ mode: 'user-workspaces' })),
updatedAt: new Date(),
})
.where(and(eq(apikey.configId, ApiKeyTemplate.WEBDAV), eq(apikey.referenceId, userId), isNull(apikey.metadata)))
const webDavKeys = await db
.select({ id: apikey.id, metadata: apikey.metadata })
.from(apikey)
.where(and(eq(apikey.configId, ApiKeyTemplate.WEBDAV), eq(apikey.referenceId, userId)))
const legacyWebDavKeyIds = webDavKeys
.filter(({ metadata }) => scopeForApiKey(ApiKeyTemplate.WEBDAV, parseMetadata(metadata)) === null)
.map(({ id }) => id)
if (legacyWebDavKeyIds.length > 0) {
await db
.update(apikey)
.set({
metadata: JSON.stringify(apiKeyMetadata({ mode: 'user-workspaces' })),
updatedAt: new Date(),
})
.where(inArray(apikey.id, legacyWebDavKeyIds))
}
const ownedOrgs = await db
.select({ orgId: member.organizationId })
@@ -78,7 +87,7 @@ export async function normalizeLegacyApiKeysForUser(db: Database, userId: string
metadata: JSON.stringify(apiKeyMetadata({ mode: 'workspace', orgId })),
updatedAt: new Date(),
})
.where(and(inArray(apikey.configId, WORKSPACE_TEMPLATES), eq(apikey.referenceId, orgId), isNull(apikey.metadata)))
.where(and(inArray(apikey.configId, WORKSPACE_TEMPLATES), eq(apikey.referenceId, orgId)))
}
}
@@ -166,11 +166,16 @@ describe('API keys', () => {
const { orgId, userId } = await getUserAndOrg(db)
const remoteDownload = await createOrgApiKey(auth, 'remote-download', orgId, userId)
const ihost = await createOrgApiKey(auth, 'ihost', orgId, userId)
// biome-ignore lint/suspicious/noExplicitAny: better-auth plugin API is not fully typed
const webdav = (await (auth.api as any).createApiKey({
body: { configId: 'webdav', userId },
})) as { id: string }
await db.run(sql`
UPDATE apikey
SET reference_id = ${orgId}, metadata = NULL
SET reference_id = ${orgId}, metadata = '{}'
WHERE id IN (${remoteDownload.id}, ${ihost.id})
`)
await db.run(sql`UPDATE apikey SET metadata = '{}' WHERE id = ${webdav.id}`)
await expect(apiKeys.verifyApiKey(auth, db, remoteDownload.key, 'remote-download')).resolves.toMatchObject({
referenceId: userId,
@@ -182,8 +187,21 @@ describe('API keys', () => {
const listResponse = await app.request('/api/auth/api-key/list', { headers })
expect(listResponse.status).toBe(200)
const listed = (await listResponse.json()) as { apiKeys: Array<{ id: string }> }
expect(listed.apiKeys.map((key) => key.id)).toEqual(expect.arrayContaining([remoteDownload.id, ihost.id]))
const listed = (await listResponse.json()) as {
apiKeys: Array<{ id: string; metadata: { scope: { mode: string; orgId?: string } } }>
}
expect(listed.apiKeys).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: ihost.id,
metadata: { scope: { mode: 'workspace', orgId } },
}),
expect.objectContaining({
id: webdav.id,
metadata: { scope: { mode: 'user-workspaces' } },
}),
]),
)
expect(await getApiKeyRow(db, ihost.id)).toMatchObject({ reference_id: userId })
})