From f3b2084b408f7d65dc583e42e47a93c69db68745 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 12:26:23 +0100 Subject: [PATCH 1/9] fix: list keys only look at externalId --- go/apps/api/routes/v2_apis_list_keys/handler.go | 6 +++--- .../db/key_list_live_by_key_space_id.sql_generated.go | 11 +++++------ go/pkg/db/querier_generated.go | 2 +- go/pkg/db/queries/key_list_live_by_key_space_id.sql | 2 +- tools/artillery/create-keys.ts | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/go/apps/api/routes/v2_apis_list_keys/handler.go b/go/apps/api/routes/v2_apis_list_keys/handler.go index 350988757e..2acb321f3d 100644 --- a/go/apps/api/routes/v2_apis_list_keys/handler.go +++ b/go/apps/api/routes/v2_apis_list_keys/handler.go @@ -161,9 +161,9 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { limit := ptr.SafeDeref(req.Limit, 100) cursor := ptr.SafeDeref(req.Cursor, "") - var identityFilter string + var externalID string if req.ExternalId != nil && *req.ExternalId != "" { - identityFilter = *req.ExternalId + externalID = *req.ExternalId } // Query keys by key_auth_id instead of api_id @@ -173,7 +173,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { db.ListLiveKeysByKeySpaceIDParams{ KeySpaceID: api.KeyAuthID.String, IDCursor: cursor, - Identity: identityFilter, + ExternalID: externalID, Limit: int32(limit + 1), // nolint:gosec }, ) diff --git a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go index d531f85134..7d016d6608 100644 --- a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go +++ b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go @@ -97,7 +97,7 @@ FROM ` + "`" + `keys` + "`" + ` k WHERE k.key_auth_id = ? AND k.id >= ? AND ( - ? = '' OR (i.external_id = ? OR i.id = ?) + ? = '' OR i.external_id = ? ) AND k.deleted_at_m IS NULL AND ka.deleted_at_m IS NULL @@ -109,7 +109,7 @@ LIMIT ? type ListLiveKeysByKeySpaceIDParams struct { KeySpaceID string `db:"key_space_id"` IDCursor string `db:"id_cursor"` - Identity string `db:"identity"` + ExternalID string `db:"external_id"` Limit int32 `db:"limit"` } @@ -236,7 +236,7 @@ type ListLiveKeysByKeySpaceIDRow struct { // WHERE k.key_auth_id = ? // AND k.id >= ? // AND ( -// ? = '' OR (i.external_id = ? OR i.id = ?) +// ? = '' OR i.external_id = ? // ) // AND k.deleted_at_m IS NULL // AND ka.deleted_at_m IS NULL @@ -247,9 +247,8 @@ func (q *Queries) ListLiveKeysByKeySpaceID(ctx context.Context, db DBTX, arg Lis rows, err := db.QueryContext(ctx, listLiveKeysByKeySpaceID, arg.KeySpaceID, arg.IDCursor, - arg.Identity, - arg.Identity, - arg.Identity, + arg.ExternalID, + arg.ExternalID, arg.Limit, ) if err != nil { diff --git a/go/pkg/db/querier_generated.go b/go/pkg/db/querier_generated.go index a19e3a22c8..a245618a23 100644 --- a/go/pkg/db/querier_generated.go +++ b/go/pkg/db/querier_generated.go @@ -1589,7 +1589,7 @@ type Querier interface { // WHERE k.key_auth_id = ? // AND k.id >= ? // AND ( - // ? = '' OR (i.external_id = ? OR i.id = ?) + // ? = '' OR i.external_id = ? // ) // AND k.deleted_at_m IS NULL // AND ka.deleted_at_m IS NULL diff --git a/go/pkg/db/queries/key_list_live_by_key_space_id.sql b/go/pkg/db/queries/key_list_live_by_key_space_id.sql index fc1a53a51d..06036345e2 100644 --- a/go/pkg/db/queries/key_list_live_by_key_space_id.sql +++ b/go/pkg/db/queries/key_list_live_by_key_space_id.sql @@ -85,7 +85,7 @@ FROM `keys` k WHERE k.key_auth_id = sqlc.arg(key_space_id) AND k.id >= sqlc.arg(id_cursor) AND ( - sqlc.arg(identity) = '' OR (i.external_id = sqlc.arg(identity) OR i.id = sqlc.arg(identity)) + sqlc.arg(external_id) = '' OR i.external_id = sqlc.arg(external_id) ) AND k.deleted_at_m IS NULL AND ka.deleted_at_m IS NULL diff --git a/tools/artillery/create-keys.ts b/tools/artillery/create-keys.ts index 520b798236..a17c90904b 100644 --- a/tools/artillery/create-keys.ts +++ b/tools/artillery/create-keys.ts @@ -152,8 +152,8 @@ async function main() { throw new Error("UNKEY_API_ID not set"); } - const totalKeys = parseInt(process.env.KEY_COUNT || "10000", 10); - const concurrency = parseInt(process.env.CONCURRENCY || "50", 10); + const totalKeys = Number.parseInt(process.env.KEY_COUNT || "10000", 10); + const concurrency = Number.parseInt(process.env.CONCURRENCY || "50", 10); console.info(`Creating ${totalKeys} keys with concurrency ${concurrency}...`); From 08513f5506ec5ed6a241da24c48707984e2d2334 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 12:46:43 +0100 Subject: [PATCH 2/9] fix: update find identity --- .../v2_identities_delete_identity/handler.go | 18 +- .../v2_identities_get_identity/handler.go | 19 +- .../v2_identities_update_identity/handler.go | 22 +-- go/pkg/db/identity_find.sql_generated.go | 122 +++++++++++++ ...tity_find_with_ratelimits.sql_generated.go | 162 ------------------ ...list_live_by_key_space_id.sql_generated.go | 10 +- go/pkg/db/querier_generated.go | 79 ++++----- go/pkg/db/queries/identity_find.sql | 33 ++++ .../queries/identity_find_with_ratelimits.sql | 45 ----- .../queries/key_list_live_by_key_space_id.sql | 5 +- 10 files changed, 227 insertions(+), 288 deletions(-) create mode 100644 go/pkg/db/identity_find.sql_generated.go delete mode 100644 go/pkg/db/identity_find_with_ratelimits.sql_generated.go create mode 100644 go/pkg/db/queries/identity_find.sql delete mode 100644 go/pkg/db/queries/identity_find_with_ratelimits.sql diff --git a/go/apps/api/routes/v2_identities_delete_identity/handler.go b/go/apps/api/routes/v2_identities_delete_identity/handler.go index 404e799cb8..20c5eb65ba 100644 --- a/go/apps/api/routes/v2_identities_delete_identity/handler.go +++ b/go/apps/api/routes/v2_identities_delete_identity/handler.go @@ -71,27 +71,25 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - results, err := db.Query.FindIdentityWithRatelimits(ctx, h.DB.RO(), db.FindIdentityWithRatelimitsParams{ + identity, err := db.Query.FindIdentity(ctx, h.DB.RO(), db.FindIdentityParams{ WorkspaceID: auth.AuthorizedWorkspaceID, Identity: req.Identity, Deleted: false, }) if err != nil { + if db.IsNotFound(err) { + return fault.New("identity not found", + fault.Code(codes.Data.Identity.NotFound.URN()), + fault.Internal("identity not found"), fault.Public("This identity does not exist."), + ) + } + return fault.Wrap(err, fault.Code(codes.App.Internal.ServiceUnavailable.URN()), fault.Internal("database failed to find the identity"), fault.Public("Error finding the identity."), ) } - if len(results) == 0 { - return fault.New("identity not found", - fault.Code(codes.Data.Identity.NotFound.URN()), - fault.Internal("identity not found"), fault.Public("This identity does not exist."), - ) - } - - identity := results[0] - // Parse ratelimits JSON var ratelimits []db.RatelimitInfo if ratelimitBytes, ok := identity.Ratelimits.([]byte); ok && ratelimitBytes != nil { diff --git a/go/apps/api/routes/v2_identities_get_identity/handler.go b/go/apps/api/routes/v2_identities_get_identity/handler.go index 42b8db51cf..a3e9177527 100644 --- a/go/apps/api/routes/v2_identities_get_identity/handler.go +++ b/go/apps/api/routes/v2_identities_get_identity/handler.go @@ -50,28 +50,25 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - results, err := db.Query.FindIdentityWithRatelimits(ctx, h.DB.RO(), db.FindIdentityWithRatelimitsParams{ + identity, err := db.Query.FindIdentity(ctx, h.DB.RO(), db.FindIdentityParams{ WorkspaceID: auth.AuthorizedWorkspaceID, Identity: req.Identity, Deleted: false, }) if err != nil { + if db.IsNotFound(err) { + return fault.New("identity not found", + fault.Code(codes.Data.Identity.NotFound.URN()), + fault.Internal("identity not found"), fault.Public("This identity does not exist."), + ) + } + return fault.Wrap(err, fault.Internal("unable to find identity"), fault.Public("We're unable to retrieve the identity."), ) } - if len(results) == 0 { - return fault.New("identity not found", - fault.Code(codes.Data.Identity.NotFound.URN()), - fault.Internal("identity not found"), - fault.Public("This identity does not exist."), - ) - } - - identity := results[0] - // Parse ratelimits JSON ratelimits, err := db.UnmarshalNullableJSONTo[[]db.RatelimitInfo](identity.Ratelimits) if err != nil { diff --git a/go/apps/api/routes/v2_identities_update_identity/handler.go b/go/apps/api/routes/v2_identities_update_identity/handler.go index 6130fe09d8..ec254884d6 100644 --- a/go/apps/api/routes/v2_identities_update_identity/handler.go +++ b/go/apps/api/routes/v2_identities_update_identity/handler.go @@ -112,29 +112,25 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } } - // Use UNION query to find identity + ratelimits in one query (fast!) - results, err := db.Query.FindIdentityWithRatelimits(ctx, h.DB.RO(), db.FindIdentityWithRatelimitsParams{ + identityRow, err := db.Query.FindIdentity(ctx, h.DB.RO(), db.FindIdentityParams{ WorkspaceID: auth.AuthorizedWorkspaceID, Identity: req.Identity, Deleted: false, }) if err != nil { + if db.IsNotFound(err) { + return fault.New("identity not found", + fault.Code(codes.Data.Identity.NotFound.URN()), + fault.Internal("identity not found"), fault.Public("This identity does not exist."), + ) + } + return fault.Wrap(err, fault.Internal("unable to find identity"), fault.Public("We're unable to retrieve the identity."), ) } - if len(results) == 0 { - return fault.New("identity not found", - fault.Code(codes.Data.Identity.NotFound.URN()), - fault.Internal("identity not found"), - fault.Public("Identity not found in this workspace"), - ) - } - - identityRow := results[0] - // Parse existing ratelimits from JSON var existingRatelimits []db.RatelimitInfo if ratelimitBytes, ok := identityRow.Ratelimits.([]byte); ok && ratelimitBytes != nil { @@ -142,7 +138,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } type txResult struct { - identity db.FindIdentityWithRatelimitsRow + identity db.FindIdentityRow finalRatelimits []openapi.RatelimitResponse } diff --git a/go/pkg/db/identity_find.sql_generated.go b/go/pkg/db/identity_find.sql_generated.go new file mode 100644 index 0000000000..72a2c8d27a --- /dev/null +++ b/go/pkg/db/identity_find.sql_generated.go @@ -0,0 +1,122 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 +// source: identity_find.sql + +package db + +import ( + "context" + "database/sql" +) + +const findIdentity = `-- name: FindIdentity :one +SELECT + i.id, i.external_id, i.workspace_id, i.environment, i.meta, i.deleted, i.created_at, i.updated_at, + COALESCE( + (SELECT JSON_ARRAYAGG( + JSON_OBJECT( + 'id', rl.id, + 'name', rl.name, + 'key_id', rl.key_id, + 'identity_id', rl.identity_id, + 'limit', rl.` + "`" + `limit` + "`" + `, + 'duration', rl.duration, + 'auto_apply', rl.auto_apply = 1 + ) + ) + FROM ratelimits rl WHERE rl.identity_id = i.id), + JSON_ARRAY() + ) as ratelimits +FROM identities i +WHERE i.id IN ( + SELECT id1.id FROM identities id1 + WHERE id1.id = ? + AND id1.workspace_id = ? + AND id1.deleted = ? + LIMIT 1 + UNION ALL + SELECT id2.id FROM identities id2 + WHERE id2.workspace_id = ? + AND id2.external_id = ? + AND id2.deleted = ? + LIMIT 1 +) +LIMIT 1 +` + +type FindIdentityParams struct { + Identity string `db:"identity"` + WorkspaceID string `db:"workspace_id"` + Deleted bool `db:"deleted"` +} + +type FindIdentityRow struct { + ID string `db:"id"` + ExternalID string `db:"external_id"` + WorkspaceID string `db:"workspace_id"` + Environment string `db:"environment"` + Meta []byte `db:"meta"` + Deleted bool `db:"deleted"` + CreatedAt int64 `db:"created_at"` + UpdatedAt sql.NullInt64 `db:"updated_at"` + Ratelimits interface{} `db:"ratelimits"` +} + +// FindIdentity +// +// SELECT +// i.id, i.external_id, i.workspace_id, i.environment, i.meta, i.deleted, i.created_at, i.updated_at, +// COALESCE( +// (SELECT JSON_ARRAYAGG( +// JSON_OBJECT( +// 'id', rl.id, +// 'name', rl.name, +// 'key_id', rl.key_id, +// 'identity_id', rl.identity_id, +// 'limit', rl.`limit`, +// 'duration', rl.duration, +// 'auto_apply', rl.auto_apply = 1 +// ) +// ) +// FROM ratelimits rl WHERE rl.identity_id = i.id), +// JSON_ARRAY() +// ) as ratelimits +// FROM identities i +// WHERE i.id IN ( +// SELECT id1.id FROM identities id1 +// WHERE id1.id = ? +// AND id1.workspace_id = ? +// AND id1.deleted = ? +// LIMIT 1 +// UNION ALL +// SELECT id2.id FROM identities id2 +// WHERE id2.workspace_id = ? +// AND id2.external_id = ? +// AND id2.deleted = ? +// LIMIT 1 +// ) +// LIMIT 1 +func (q *Queries) FindIdentity(ctx context.Context, db DBTX, arg FindIdentityParams) (FindIdentityRow, error) { + row := db.QueryRowContext(ctx, findIdentity, + arg.Identity, + arg.WorkspaceID, + arg.Deleted, + arg.WorkspaceID, + arg.Identity, + arg.Deleted, + ) + var i FindIdentityRow + err := row.Scan( + &i.ID, + &i.ExternalID, + &i.WorkspaceID, + &i.Environment, + &i.Meta, + &i.Deleted, + &i.CreatedAt, + &i.UpdatedAt, + &i.Ratelimits, + ) + return i, err +} diff --git a/go/pkg/db/identity_find_with_ratelimits.sql_generated.go b/go/pkg/db/identity_find_with_ratelimits.sql_generated.go deleted file mode 100644 index 92558675b4..0000000000 --- a/go/pkg/db/identity_find_with_ratelimits.sql_generated.go +++ /dev/null @@ -1,162 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: identity_find_with_ratelimits.sql - -package db - -import ( - "context" - "database/sql" -) - -const findIdentityWithRatelimits = `-- name: FindIdentityWithRatelimits :many -SELECT - i.id, i.external_id, i.workspace_id, i.environment, i.meta, i.deleted, i.created_at, i.updated_at, - COALESCE( - (SELECT JSON_ARRAYAGG( - JSON_OBJECT( - 'id', rl.id, - 'name', rl.name, - 'key_id', rl.key_id, - 'identity_id', rl.identity_id, - 'limit', rl.` + "`" + `limit` + "`" + `, - 'duration', rl.duration, - 'auto_apply', rl.auto_apply = 1 - ) - ) - FROM ratelimits rl WHERE rl.identity_id = i.id), - JSON_ARRAY() - ) as ratelimits -FROM identities i -WHERE i.workspace_id = ? - AND i.id = ? - AND i.deleted = ? -UNION ALL -SELECT - i.id, i.external_id, i.workspace_id, i.environment, i.meta, i.deleted, i.created_at, i.updated_at, - COALESCE( - (SELECT JSON_ARRAYAGG( - JSON_OBJECT( - 'id', rl.id, - 'name', rl.name, - 'key_id', rl.key_id, - 'identity_id', rl.identity_id, - 'limit', rl.` + "`" + `limit` + "`" + `, - 'duration', rl.duration, - 'auto_apply', rl.auto_apply = 1 - ) - ) - FROM ratelimits rl WHERE rl.identity_id = i.id), - JSON_ARRAY() - ) as ratelimits -FROM identities i -WHERE i.workspace_id = ? - AND i.external_id = ? - AND i.deleted = ? -LIMIT 1 -` - -type FindIdentityWithRatelimitsParams struct { - WorkspaceID string `db:"workspace_id"` - Identity string `db:"identity"` - Deleted bool `db:"deleted"` -} - -type FindIdentityWithRatelimitsRow struct { - ID string `db:"id"` - ExternalID string `db:"external_id"` - WorkspaceID string `db:"workspace_id"` - Environment string `db:"environment"` - Meta []byte `db:"meta"` - Deleted bool `db:"deleted"` - CreatedAt int64 `db:"created_at"` - UpdatedAt sql.NullInt64 `db:"updated_at"` - Ratelimits interface{} `db:"ratelimits"` -} - -// FindIdentityWithRatelimits -// -// SELECT -// i.id, i.external_id, i.workspace_id, i.environment, i.meta, i.deleted, i.created_at, i.updated_at, -// COALESCE( -// (SELECT JSON_ARRAYAGG( -// JSON_OBJECT( -// 'id', rl.id, -// 'name', rl.name, -// 'key_id', rl.key_id, -// 'identity_id', rl.identity_id, -// 'limit', rl.`limit`, -// 'duration', rl.duration, -// 'auto_apply', rl.auto_apply = 1 -// ) -// ) -// FROM ratelimits rl WHERE rl.identity_id = i.id), -// JSON_ARRAY() -// ) as ratelimits -// FROM identities i -// WHERE i.workspace_id = ? -// AND i.id = ? -// AND i.deleted = ? -// UNION ALL -// SELECT -// i.id, i.external_id, i.workspace_id, i.environment, i.meta, i.deleted, i.created_at, i.updated_at, -// COALESCE( -// (SELECT JSON_ARRAYAGG( -// JSON_OBJECT( -// 'id', rl.id, -// 'name', rl.name, -// 'key_id', rl.key_id, -// 'identity_id', rl.identity_id, -// 'limit', rl.`limit`, -// 'duration', rl.duration, -// 'auto_apply', rl.auto_apply = 1 -// ) -// ) -// FROM ratelimits rl WHERE rl.identity_id = i.id), -// JSON_ARRAY() -// ) as ratelimits -// FROM identities i -// WHERE i.workspace_id = ? -// AND i.external_id = ? -// AND i.deleted = ? -// LIMIT 1 -func (q *Queries) FindIdentityWithRatelimits(ctx context.Context, db DBTX, arg FindIdentityWithRatelimitsParams) ([]FindIdentityWithRatelimitsRow, error) { - rows, err := db.QueryContext(ctx, findIdentityWithRatelimits, - arg.WorkspaceID, - arg.Identity, - arg.Deleted, - arg.WorkspaceID, - arg.Identity, - arg.Deleted, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []FindIdentityWithRatelimitsRow - for rows.Next() { - var i FindIdentityWithRatelimitsRow - if err := rows.Scan( - &i.ID, - &i.ExternalID, - &i.WorkspaceID, - &i.Environment, - &i.Meta, - &i.Deleted, - &i.CreatedAt, - &i.UpdatedAt, - &i.Ratelimits, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go index 7d016d6608..bcbeb9cde4 100644 --- a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go +++ b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go @@ -97,7 +97,10 @@ FROM ` + "`" + `keys` + "`" + ` k WHERE k.key_auth_id = ? AND k.id >= ? AND ( - ? = '' OR i.external_id = ? + ? = '' OR ( + i.workspace_id = k.workspace_id + AND i.external_id = ? + ) ) AND k.deleted_at_m IS NULL AND ka.deleted_at_m IS NULL @@ -236,7 +239,10 @@ type ListLiveKeysByKeySpaceIDRow struct { // WHERE k.key_auth_id = ? // AND k.id >= ? // AND ( -// ? = '' OR i.external_id = ? +// ? = '' OR ( +// i.workspace_id = k.workspace_id +// AND i.external_id = ? +// ) // ) // AND k.deleted_at_m IS NULL // AND ka.deleted_at_m IS NULL diff --git a/go/pkg/db/querier_generated.go b/go/pkg/db/querier_generated.go index a245618a23..5c2c522aff 100644 --- a/go/pkg/db/querier_generated.go +++ b/go/pkg/db/querier_generated.go @@ -278,23 +278,7 @@ type Querier interface { // AND deleted = ? // AND (external_id IN(/*SLICE:identities*/?) OR id IN (/*SLICE:identities*/?)) FindIdentities(ctx context.Context, db DBTX, arg FindIdentitiesParams) ([]Identity, error) - //FindIdentityByExternalID - // - // SELECT id, external_id, workspace_id, environment, meta, deleted, created_at, updated_at - // FROM identities - // WHERE workspace_id = ? - // AND external_id = ? - // AND deleted = ? - FindIdentityByExternalID(ctx context.Context, db DBTX, arg FindIdentityByExternalIDParams) (Identity, error) - //FindIdentityByID - // - // SELECT id, external_id, workspace_id, environment, meta, deleted, created_at, updated_at - // FROM identities - // WHERE workspace_id = ? - // AND id = ? - // AND deleted = ? - FindIdentityByID(ctx context.Context, db DBTX, arg FindIdentityByIDParams) (Identity, error) - //FindIdentityWithRatelimits + //FindIdentity // // SELECT // i.id, i.external_id, i.workspace_id, i.environment, i.meta, i.deleted, i.created_at, i.updated_at, @@ -314,33 +298,37 @@ type Querier interface { // JSON_ARRAY() // ) as ratelimits // FROM identities i - // WHERE i.workspace_id = ? - // AND i.id = ? - // AND i.deleted = ? - // UNION ALL - // SELECT - // i.id, i.external_id, i.workspace_id, i.environment, i.meta, i.deleted, i.created_at, i.updated_at, - // COALESCE( - // (SELECT JSON_ARRAYAGG( - // JSON_OBJECT( - // 'id', rl.id, - // 'name', rl.name, - // 'key_id', rl.key_id, - // 'identity_id', rl.identity_id, - // 'limit', rl.`limit`, - // 'duration', rl.duration, - // 'auto_apply', rl.auto_apply = 1 - // ) - // ) - // FROM ratelimits rl WHERE rl.identity_id = i.id), - // JSON_ARRAY() - // ) as ratelimits - // FROM identities i - // WHERE i.workspace_id = ? - // AND i.external_id = ? - // AND i.deleted = ? + // WHERE i.id IN ( + // SELECT id1.id FROM identities id1 + // WHERE id1.id = ? + // AND id1.workspace_id = ? + // AND id1.deleted = ? + // LIMIT 1 + // UNION ALL + // SELECT id2.id FROM identities id2 + // WHERE id2.workspace_id = ? + // AND id2.external_id = ? + // AND id2.deleted = ? + // LIMIT 1 + // ) // LIMIT 1 - FindIdentityWithRatelimits(ctx context.Context, db DBTX, arg FindIdentityWithRatelimitsParams) ([]FindIdentityWithRatelimitsRow, error) + FindIdentity(ctx context.Context, db DBTX, arg FindIdentityParams) (FindIdentityRow, error) + //FindIdentityByExternalID + // + // SELECT id, external_id, workspace_id, environment, meta, deleted, created_at, updated_at + // FROM identities + // WHERE workspace_id = ? + // AND external_id = ? + // AND deleted = ? + FindIdentityByExternalID(ctx context.Context, db DBTX, arg FindIdentityByExternalIDParams) (Identity, error) + //FindIdentityByID + // + // SELECT id, external_id, workspace_id, environment, meta, deleted, created_at, updated_at + // FROM identities + // WHERE workspace_id = ? + // AND id = ? + // AND deleted = ? + FindIdentityByID(ctx context.Context, db DBTX, arg FindIdentityByIDParams) (Identity, error) //FindKeyAuthsByIds // // SELECT ka.id as key_auth_id, a.id as api_id @@ -1589,7 +1577,10 @@ type Querier interface { // WHERE k.key_auth_id = ? // AND k.id >= ? // AND ( - // ? = '' OR i.external_id = ? + // ? = '' OR ( + // i.workspace_id = k.workspace_id + // AND i.external_id = ? + // ) // ) // AND k.deleted_at_m IS NULL // AND ka.deleted_at_m IS NULL diff --git a/go/pkg/db/queries/identity_find.sql b/go/pkg/db/queries/identity_find.sql new file mode 100644 index 0000000000..feaef9b3da --- /dev/null +++ b/go/pkg/db/queries/identity_find.sql @@ -0,0 +1,33 @@ +-- name: FindIdentity :one +SELECT + i.*, + COALESCE( + (SELECT JSON_ARRAYAGG( + JSON_OBJECT( + 'id', rl.id, + 'name', rl.name, + 'key_id', rl.key_id, + 'identity_id', rl.identity_id, + 'limit', rl.`limit`, + 'duration', rl.duration, + 'auto_apply', rl.auto_apply = 1 + ) + ) + FROM ratelimits rl WHERE rl.identity_id = i.id), + JSON_ARRAY() + ) as ratelimits +FROM identities i +WHERE i.id IN ( + SELECT id1.id FROM identities id1 + WHERE id1.id = sqlc.arg(identity) + AND id1.workspace_id = sqlc.arg(workspace_id) + AND id1.deleted = sqlc.arg(deleted) + LIMIT 1 + UNION ALL + SELECT id2.id FROM identities id2 + WHERE id2.workspace_id = sqlc.arg(workspace_id) + AND id2.external_id = sqlc.arg(identity) + AND id2.deleted = sqlc.arg(deleted) + LIMIT 1 +) +LIMIT 1; diff --git a/go/pkg/db/queries/identity_find_with_ratelimits.sql b/go/pkg/db/queries/identity_find_with_ratelimits.sql deleted file mode 100644 index 9f7f24d457..0000000000 --- a/go/pkg/db/queries/identity_find_with_ratelimits.sql +++ /dev/null @@ -1,45 +0,0 @@ --- name: FindIdentityWithRatelimits :many -SELECT - i.*, - COALESCE( - (SELECT JSON_ARRAYAGG( - JSON_OBJECT( - 'id', rl.id, - 'name', rl.name, - 'key_id', rl.key_id, - 'identity_id', rl.identity_id, - 'limit', rl.`limit`, - 'duration', rl.duration, - 'auto_apply', rl.auto_apply = 1 - ) - ) - FROM ratelimits rl WHERE rl.identity_id = i.id), - JSON_ARRAY() - ) as ratelimits -FROM identities i -WHERE i.workspace_id = sqlc.arg(workspace_id) - AND i.id = sqlc.arg(identity) - AND i.deleted = sqlc.arg(deleted) -UNION ALL -SELECT - i.*, - COALESCE( - (SELECT JSON_ARRAYAGG( - JSON_OBJECT( - 'id', rl.id, - 'name', rl.name, - 'key_id', rl.key_id, - 'identity_id', rl.identity_id, - 'limit', rl.`limit`, - 'duration', rl.duration, - 'auto_apply', rl.auto_apply = 1 - ) - ) - FROM ratelimits rl WHERE rl.identity_id = i.id), - JSON_ARRAY() - ) as ratelimits -FROM identities i -WHERE i.workspace_id = sqlc.arg(workspace_id) - AND i.external_id = sqlc.arg(identity) - AND i.deleted = sqlc.arg(deleted) -LIMIT 1; diff --git a/go/pkg/db/queries/key_list_live_by_key_space_id.sql b/go/pkg/db/queries/key_list_live_by_key_space_id.sql index 06036345e2..ea76ef0614 100644 --- a/go/pkg/db/queries/key_list_live_by_key_space_id.sql +++ b/go/pkg/db/queries/key_list_live_by_key_space_id.sql @@ -85,7 +85,10 @@ FROM `keys` k WHERE k.key_auth_id = sqlc.arg(key_space_id) AND k.id >= sqlc.arg(id_cursor) AND ( - sqlc.arg(external_id) = '' OR i.external_id = sqlc.arg(external_id) + sqlc.arg(external_id) = '' OR ( + i.workspace_id = k.workspace_id + AND i.external_id = sqlc.arg(external_id) + ) ) AND k.deleted_at_m IS NULL AND ka.deleted_at_m IS NULL From ee0b06fca7da2d1b987aab5919f7e19d28142d19 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 13:04:32 +0100 Subject: [PATCH 3/9] fix: use straigt join --- go/pkg/db/key_list_live_by_key_space_id.sql_generated.go | 8 ++++---- go/pkg/db/querier_generated.go | 4 ++-- go/pkg/db/queries/key_list_live_by_key_space_id.sql | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go index bcbeb9cde4..a016b18acb 100644 --- a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go +++ b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go @@ -90,8 +90,8 @@ SELECT k.id, k.key_auth_id, k.hash, k.start, k.workspace_id, k.for_workspace_id, JSON_ARRAY() ) AS ratelimits FROM ` + "`" + `keys` + "`" + ` k - JOIN key_auth ka ON ka.id = k.key_auth_id - JOIN workspaces ws ON ws.id = k.workspace_id + STRAIGHT_JOIN key_auth ka ON ka.id = k.key_auth_id + STRAIGHT_JOIN workspaces ws ON ws.id = k.workspace_id LEFT JOIN identities i ON k.identity_id = i.id AND i.deleted = false LEFT JOIN encrypted_keys ek ON ek.key_id = k.id WHERE k.key_auth_id = ? @@ -232,8 +232,8 @@ type ListLiveKeysByKeySpaceIDRow struct { // JSON_ARRAY() // ) AS ratelimits // FROM `keys` k -// JOIN key_auth ka ON ka.id = k.key_auth_id -// JOIN workspaces ws ON ws.id = k.workspace_id +// STRAIGHT_JOIN key_auth ka ON ka.id = k.key_auth_id +// STRAIGHT_JOIN workspaces ws ON ws.id = k.workspace_id // LEFT JOIN identities i ON k.identity_id = i.id AND i.deleted = false // LEFT JOIN encrypted_keys ek ON ek.key_id = k.id // WHERE k.key_auth_id = ? diff --git a/go/pkg/db/querier_generated.go b/go/pkg/db/querier_generated.go index 5c2c522aff..96891ad49d 100644 --- a/go/pkg/db/querier_generated.go +++ b/go/pkg/db/querier_generated.go @@ -1570,8 +1570,8 @@ type Querier interface { // JSON_ARRAY() // ) AS ratelimits // FROM `keys` k - // JOIN key_auth ka ON ka.id = k.key_auth_id - // JOIN workspaces ws ON ws.id = k.workspace_id + // STRAIGHT_JOIN key_auth ka ON ka.id = k.key_auth_id + // STRAIGHT_JOIN workspaces ws ON ws.id = k.workspace_id // LEFT JOIN identities i ON k.identity_id = i.id AND i.deleted = false // LEFT JOIN encrypted_keys ek ON ek.key_id = k.id // WHERE k.key_auth_id = ? diff --git a/go/pkg/db/queries/key_list_live_by_key_space_id.sql b/go/pkg/db/queries/key_list_live_by_key_space_id.sql index ea76ef0614..dd95d72db3 100644 --- a/go/pkg/db/queries/key_list_live_by_key_space_id.sql +++ b/go/pkg/db/queries/key_list_live_by_key_space_id.sql @@ -78,8 +78,8 @@ SELECT k.*, JSON_ARRAY() ) AS ratelimits FROM `keys` k - JOIN key_auth ka ON ka.id = k.key_auth_id - JOIN workspaces ws ON ws.id = k.workspace_id + STRAIGHT_JOIN key_auth ka ON ka.id = k.key_auth_id + STRAIGHT_JOIN workspaces ws ON ws.id = k.workspace_id LEFT JOIN identities i ON k.identity_id = i.id AND i.deleted = false LEFT JOIN encrypted_keys ek ON ek.key_id = k.id WHERE k.key_auth_id = sqlc.arg(key_space_id) From 56f77cadda76062356f976bd1425a5bcc8ac6bb5 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 13:13:40 +0100 Subject: [PATCH 4/9] fix: use derived table --- go/pkg/db/identity_find.sql_generated.go | 12 ++++-------- go/pkg/db/querier_generated.go | 6 ++---- go/pkg/db/queries/identity_find.sql | 6 ++---- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/go/pkg/db/identity_find.sql_generated.go b/go/pkg/db/identity_find.sql_generated.go index 72a2c8d27a..95bbfb6917 100644 --- a/go/pkg/db/identity_find.sql_generated.go +++ b/go/pkg/db/identity_find.sql_generated.go @@ -29,19 +29,17 @@ SELECT JSON_ARRAY() ) as ratelimits FROM identities i -WHERE i.id IN ( +JOIN ( SELECT id1.id FROM identities id1 WHERE id1.id = ? AND id1.workspace_id = ? AND id1.deleted = ? - LIMIT 1 UNION ALL SELECT id2.id FROM identities id2 WHERE id2.workspace_id = ? AND id2.external_id = ? AND id2.deleted = ? - LIMIT 1 -) +) AS identity_lookup ON i.id = identity_lookup.id LIMIT 1 ` @@ -83,19 +81,17 @@ type FindIdentityRow struct { // JSON_ARRAY() // ) as ratelimits // FROM identities i -// WHERE i.id IN ( +// JOIN ( // SELECT id1.id FROM identities id1 // WHERE id1.id = ? // AND id1.workspace_id = ? // AND id1.deleted = ? -// LIMIT 1 // UNION ALL // SELECT id2.id FROM identities id2 // WHERE id2.workspace_id = ? // AND id2.external_id = ? // AND id2.deleted = ? -// LIMIT 1 -// ) +// ) AS identity_lookup ON i.id = identity_lookup.id // LIMIT 1 func (q *Queries) FindIdentity(ctx context.Context, db DBTX, arg FindIdentityParams) (FindIdentityRow, error) { row := db.QueryRowContext(ctx, findIdentity, diff --git a/go/pkg/db/querier_generated.go b/go/pkg/db/querier_generated.go index 96891ad49d..6802c4488f 100644 --- a/go/pkg/db/querier_generated.go +++ b/go/pkg/db/querier_generated.go @@ -298,19 +298,17 @@ type Querier interface { // JSON_ARRAY() // ) as ratelimits // FROM identities i - // WHERE i.id IN ( + // JOIN ( // SELECT id1.id FROM identities id1 // WHERE id1.id = ? // AND id1.workspace_id = ? // AND id1.deleted = ? - // LIMIT 1 // UNION ALL // SELECT id2.id FROM identities id2 // WHERE id2.workspace_id = ? // AND id2.external_id = ? // AND id2.deleted = ? - // LIMIT 1 - // ) + // ) AS identity_lookup ON i.id = identity_lookup.id // LIMIT 1 FindIdentity(ctx context.Context, db DBTX, arg FindIdentityParams) (FindIdentityRow, error) //FindIdentityByExternalID diff --git a/go/pkg/db/queries/identity_find.sql b/go/pkg/db/queries/identity_find.sql index feaef9b3da..8e06809cfc 100644 --- a/go/pkg/db/queries/identity_find.sql +++ b/go/pkg/db/queries/identity_find.sql @@ -17,17 +17,15 @@ SELECT JSON_ARRAY() ) as ratelimits FROM identities i -WHERE i.id IN ( +JOIN ( SELECT id1.id FROM identities id1 WHERE id1.id = sqlc.arg(identity) AND id1.workspace_id = sqlc.arg(workspace_id) AND id1.deleted = sqlc.arg(deleted) - LIMIT 1 UNION ALL SELECT id2.id FROM identities id2 WHERE id2.workspace_id = sqlc.arg(workspace_id) AND id2.external_id = sqlc.arg(identity) AND id2.deleted = sqlc.arg(deleted) - LIMIT 1 -) +) AS identity_lookup ON i.id = identity_lookup.id LIMIT 1; From 1d8969f063efebbfac3759f012f7f5f349648605 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 13:16:14 +0100 Subject: [PATCH 5/9] fix: test --- go/apps/api/routes/v2_identities_update_identity/404_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/apps/api/routes/v2_identities_update_identity/404_test.go b/go/apps/api/routes/v2_identities_update_identity/404_test.go index 4e6d39c562..2fb61dddf1 100644 --- a/go/apps/api/routes/v2_identities_update_identity/404_test.go +++ b/go/apps/api/routes/v2_identities_update_identity/404_test.go @@ -41,7 +41,7 @@ func TestNotFound(t *testing.T) { res := testutil.CallRoute[handler.Request, openapi.NotFoundErrorResponse](h, route, headers, req) require.Equal(t, http.StatusNotFound, res.Status, "expected 404, got: %d", res.Status) require.Equal(t, "https://unkey.com/docs/errors/unkey/data/identity_not_found", res.Body.Error.Type) - require.Equal(t, "Identity not found in this workspace", res.Body.Error.Detail) + require.Equal(t, "This identity does not exist.", res.Body.Error.Detail) require.Equal(t, http.StatusNotFound, res.Body.Error.Status) require.Equal(t, "Not Found", res.Body.Error.Title) require.NotEmpty(t, res.Body.Meta.RequestId) From 7b1bf66c4e648236211e235181544333bfce4963 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 14:03:28 +0100 Subject: [PATCH 6/9] fix: factor test and look up identity first --- .../api/routes/v2_apis_list_keys/200_test.go | 164 ++++++------------ .../api/routes/v2_apis_list_keys/handler.go | 38 +++- ...list_live_by_key_space_id.sql_generated.go | 26 +-- go/pkg/db/querier_generated.go | 7 +- .../queries/key_list_live_by_key_space_id.sql | 7 +- 5 files changed, 100 insertions(+), 142 deletions(-) diff --git a/go/apps/api/routes/v2_apis_list_keys/200_test.go b/go/apps/api/routes/v2_apis_list_keys/200_test.go index 40d6a5587a..bad6a4c7b5 100644 --- a/go/apps/api/routes/v2_apis_list_keys/200_test.go +++ b/go/apps/api/routes/v2_apis_list_keys/200_test.go @@ -3,7 +3,6 @@ package handler_test import ( "context" "database/sql" - "encoding/json" "fmt" "net/http" "testing" @@ -15,9 +14,8 @@ import ( "github.com/unkeyed/unkey/go/pkg/hash" "github.com/unkeyed/unkey/go/pkg/ptr" "github.com/unkeyed/unkey/go/pkg/testutil" + "github.com/unkeyed/unkey/go/pkg/testutil/seed" "github.com/unkeyed/unkey/go/pkg/uid" - - vaultv1 "github.com/unkeyed/unkey/go/gen/proto/vault/v1" ) func TestSuccess(t *testing.T) { @@ -70,127 +68,75 @@ func TestSuccess(t *testing.T) { require.NoError(t, err) // Create test identities - identity1ID := uid.New("identity") identity1ExternalID := "test_user_1" - err = db.Query.InsertIdentity(ctx, h.DB.RW(), db.InsertIdentityParams{ - ID: identity1ID, - ExternalID: identity1ExternalID, + identity1 := h.CreateIdentity(seed.CreateIdentityRequest{ WorkspaceID: workspace.ID, - Environment: "", - CreatedAt: time.Now().UnixMilli(), + ExternalID: identity1ExternalID, Meta: []byte(`{"role": "admin"}`), }) - require.NoError(t, err) - identity2ID := uid.New("identity") identity2ExternalID := "test_user_2" - err = db.Query.InsertIdentity(ctx, h.DB.RW(), db.InsertIdentityParams{ - ID: identity2ID, - ExternalID: identity2ExternalID, + identity2 := h.CreateIdentity(seed.CreateIdentityRequest{ WorkspaceID: workspace.ID, - Environment: "", - CreatedAt: time.Now().UnixMilli(), + ExternalID: identity2ExternalID, Meta: []byte(`{"role": "user"}`), }) - require.NoError(t, err) - encryptedKeysMap := make(map[string]struct{}) // Create test keys with various configurations - testKeys := []struct { - id string - start string - name string - identityID *string - meta map[string]interface{} - expires *time.Time - enabled bool - }{ - { - id: uid.New("key"), - start: "test_key1_", - name: "Test Key 1", - identityID: &identity1ID, - meta: map[string]interface{}{"env": "production", "team": "backend"}, - enabled: true, - }, - { - id: uid.New("key"), - start: "test_key2_", - name: "Test Key 2", - identityID: &identity1ID, - meta: map[string]interface{}{"env": "staging"}, - enabled: true, - }, - { - id: uid.New("key"), - start: "test_key3_", - name: "Test Key 3", - identityID: &identity2ID, - meta: map[string]interface{}{"env": "development"}, - enabled: true, - }, - { - id: uid.New("key"), - start: "test_key4_", - name: "Test Key 4 (No Identity)", - enabled: true, - }, - { - id: uid.New("key"), - start: "test_key5_", - name: "Test Key 5 (Disabled)", - enabled: false, - }, - } - - for _, keyData := range testKeys { - metaBytes := []byte("{}") - if keyData.meta != nil { - metaBytes, _ = json.Marshal(keyData.meta) - } - - key := keyData.start + uid.New("") + // Track encrypted keys for verification + encryptedKeys := make(map[string]string) // keyID -> plaintext - insertParams := db.InsertKeyParams{ - ID: keyData.id, - KeySpaceID: keySpaceID, - Hash: hash.Sha256(key), - Start: keyData.start, - WorkspaceID: workspace.ID, - ForWorkspaceID: sql.NullString{Valid: false}, - Name: sql.NullString{Valid: true, String: keyData.name}, - Meta: sql.NullString{Valid: true, String: string(metaBytes)}, - Expires: sql.NullTime{Valid: false}, - CreatedAtM: time.Now().UnixMilli(), - Enabled: keyData.enabled, - RemainingRequests: sql.NullInt32{Valid: false}, - } + // Key 1: identity1, production metadata + key1 := h.CreateKey(seed.CreateKeyRequest{ + WorkspaceID: workspace.ID, + KeySpaceID: keySpaceID, + Name: ptr.P("Test Key 1"), + IdentityID: ptr.P(identity1.ID), + Meta: ptr.P(`{"env": "production", "team": "backend"}`), + Recoverable: true, + }) + encryptedKeys[key1.KeyID] = key1.Key - if keyData.identityID != nil { - insertParams.IdentityID = sql.NullString{Valid: true, String: *keyData.identityID} - } else { - insertParams.IdentityID = sql.NullString{Valid: false} - } + // Key 2: identity1, staging metadata + key2 := h.CreateKey(seed.CreateKeyRequest{ + WorkspaceID: workspace.ID, + KeySpaceID: keySpaceID, + Name: ptr.P("Test Key 2"), + IdentityID: ptr.P(identity1.ID), + Meta: ptr.P(`{"env": "staging"}`), + Recoverable: true, + }) + encryptedKeys[key2.KeyID] = key2.Key - err := db.Query.InsertKey(ctx, h.DB.RW(), insertParams) - require.NoError(t, err) + // Key 3: identity2, development metadata + key3 := h.CreateKey(seed.CreateKeyRequest{ + WorkspaceID: workspace.ID, + KeySpaceID: keySpaceID, + Name: ptr.P("Test Key 3"), + IdentityID: ptr.P(identity2.ID), + Meta: ptr.P(`{"env": "development"}`), + Recoverable: true, + }) + encryptedKeys[key3.KeyID] = key3.Key - encryption, err := h.Vault.Encrypt(ctx, &vaultv1.EncryptRequest{ - Keyring: h.Resources().UserWorkspace.ID, - Data: key, - }) - require.NoError(t, err) + // Key 4: no identity + key4 := h.CreateKey(seed.CreateKeyRequest{ + WorkspaceID: workspace.ID, + KeySpaceID: keySpaceID, + Name: ptr.P("Test Key 4 (No Identity)"), + Recoverable: true, + }) + encryptedKeys[key4.KeyID] = key4.Key - err = db.Query.InsertKeyEncryption(ctx, h.DB.RW(), db.InsertKeyEncryptionParams{ - WorkspaceID: h.Resources().UserWorkspace.ID, - KeyID: keyData.id, - CreatedAt: time.Now().UnixMilli(), - Encrypted: encryption.GetEncrypted(), - EncryptionKeyID: encryption.GetKeyId(), - }) - require.NoError(t, err) - encryptedKeysMap[keyData.id] = struct{}{} - } + // Key 5: disabled + key5 := h.CreateKey(seed.CreateKeyRequest{ + WorkspaceID: workspace.ID, + KeySpaceID: keySpaceID, + Name: ptr.P("Test Key 5 (Disabled)"), + Disabled: true, + Recoverable: true, + }) + encryptedKeys[key5.KeyID] = key5.Key // Set up request headers headers := http.Header{ @@ -604,7 +550,7 @@ func TestSuccess(t *testing.T) { require.NotNil(t, res.Body.Data) for _, key := range res.Body.Data { - _, exists := encryptedKeysMap[key.KeyId] + expectedPlaintext, exists := encryptedKeys[key.KeyId] if !exists { continue } diff --git a/go/apps/api/routes/v2_apis_list_keys/handler.go b/go/apps/api/routes/v2_apis_list_keys/handler.go index 2acb321f3d..e59314e636 100644 --- a/go/apps/api/routes/v2_apis_list_keys/handler.go +++ b/go/apps/api/routes/v2_apis_list_keys/handler.go @@ -2,6 +2,7 @@ package handler import ( "context" + "database/sql" "net/http" "github.com/oapi-codegen/nullable" @@ -161,19 +162,50 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { limit := ptr.SafeDeref(req.Limit, 100) cursor := ptr.SafeDeref(req.Cursor, "") - var externalID string + // Resolve identity ID if external_id filter is provided + var identityID string if req.ExternalId != nil && *req.ExternalId != "" { - externalID = *req.ExternalId + identity, identityErr := db.Query.FindIdentityByExternalID(ctx, h.DB.RO(), db.FindIdentityByExternalIDParams{ + WorkspaceID: auth.AuthorizedWorkspaceID, + ExternalID: *req.ExternalId, + Deleted: false, + }) + if identityErr != nil { + if db.IsNotFound(identityErr) { + // Identity doesn't exist, return empty result set + return s.JSON(http.StatusOK, Response{ + Meta: openapi.Meta{ + RequestId: s.RequestID(), + }, + Data: []openapi.KeyResponseData{}, + Pagination: &openapi.Pagination{ + Cursor: nil, + HasMore: false, + }, + }) + } + return fault.Wrap(identityErr, + fault.Code(codes.App.Internal.ServiceUnavailable.URN()), + fault.Internal("database error"), + fault.Public("Failed to retrieve identity."), + ) + } + identityID = identity.ID } // Query keys by key_auth_id instead of api_id + var identityIDParam sql.NullString + if identityID != "" { + identityIDParam = sql.NullString{String: identityID, Valid: true} + } + keyResults, err := db.Query.ListLiveKeysByKeySpaceID( ctx, h.DB.RO(), db.ListLiveKeysByKeySpaceIDParams{ KeySpaceID: api.KeyAuthID.String, IDCursor: cursor, - ExternalID: externalID, + IdentityID: identityIDParam, Limit: int32(limit + 1), // nolint:gosec }, ) diff --git a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go index a016b18acb..935332f804 100644 --- a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go +++ b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go @@ -96,12 +96,7 @@ FROM ` + "`" + `keys` + "`" + ` k LEFT JOIN encrypted_keys ek ON ek.key_id = k.id WHERE k.key_auth_id = ? AND k.id >= ? - AND ( - ? = '' OR ( - i.workspace_id = k.workspace_id - AND i.external_id = ? - ) - ) + AND (? IS NULL OR k.identity_id = ?) AND k.deleted_at_m IS NULL AND ka.deleted_at_m IS NULL AND ws.deleted_at_m IS NULL @@ -110,10 +105,10 @@ LIMIT ? ` type ListLiveKeysByKeySpaceIDParams struct { - KeySpaceID string `db:"key_space_id"` - IDCursor string `db:"id_cursor"` - ExternalID string `db:"external_id"` - Limit int32 `db:"limit"` + KeySpaceID string `db:"key_space_id"` + IDCursor string `db:"id_cursor"` + IdentityID sql.NullString `db:"identity_id"` + Limit int32 `db:"limit"` } type ListLiveKeysByKeySpaceIDRow struct { @@ -238,12 +233,7 @@ type ListLiveKeysByKeySpaceIDRow struct { // LEFT JOIN encrypted_keys ek ON ek.key_id = k.id // WHERE k.key_auth_id = ? // AND k.id >= ? -// AND ( -// ? = '' OR ( -// i.workspace_id = k.workspace_id -// AND i.external_id = ? -// ) -// ) +// AND (? IS NULL OR k.identity_id = ?) // AND k.deleted_at_m IS NULL // AND ka.deleted_at_m IS NULL // AND ws.deleted_at_m IS NULL @@ -253,8 +243,8 @@ func (q *Queries) ListLiveKeysByKeySpaceID(ctx context.Context, db DBTX, arg Lis rows, err := db.QueryContext(ctx, listLiveKeysByKeySpaceID, arg.KeySpaceID, arg.IDCursor, - arg.ExternalID, - arg.ExternalID, + arg.IdentityID, + arg.IdentityID, arg.Limit, ) if err != nil { diff --git a/go/pkg/db/querier_generated.go b/go/pkg/db/querier_generated.go index 6802c4488f..06608a447d 100644 --- a/go/pkg/db/querier_generated.go +++ b/go/pkg/db/querier_generated.go @@ -1574,12 +1574,7 @@ type Querier interface { // LEFT JOIN encrypted_keys ek ON ek.key_id = k.id // WHERE k.key_auth_id = ? // AND k.id >= ? - // AND ( - // ? = '' OR ( - // i.workspace_id = k.workspace_id - // AND i.external_id = ? - // ) - // ) + // AND (? IS NULL OR k.identity_id = ?) // AND k.deleted_at_m IS NULL // AND ka.deleted_at_m IS NULL // AND ws.deleted_at_m IS NULL diff --git a/go/pkg/db/queries/key_list_live_by_key_space_id.sql b/go/pkg/db/queries/key_list_live_by_key_space_id.sql index dd95d72db3..7c876d791d 100644 --- a/go/pkg/db/queries/key_list_live_by_key_space_id.sql +++ b/go/pkg/db/queries/key_list_live_by_key_space_id.sql @@ -84,12 +84,7 @@ FROM `keys` k LEFT JOIN encrypted_keys ek ON ek.key_id = k.id WHERE k.key_auth_id = sqlc.arg(key_space_id) AND k.id >= sqlc.arg(id_cursor) - AND ( - sqlc.arg(external_id) = '' OR ( - i.workspace_id = k.workspace_id - AND i.external_id = sqlc.arg(external_id) - ) - ) + AND (sqlc.arg(identity_id) IS NULL OR k.identity_id = sqlc.arg(identity_id)) AND k.deleted_at_m IS NULL AND ka.deleted_at_m IS NULL AND ws.deleted_at_m IS NULL From 2fe20b508457efaa769cccc1e844e7ff3b85f719 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 14:06:10 +0100 Subject: [PATCH 7/9] fix: factor test and look up identity first --- go/apps/api/routes/v2_apis_list_keys/200_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/apps/api/routes/v2_apis_list_keys/200_test.go b/go/apps/api/routes/v2_apis_list_keys/200_test.go index bad6a4c7b5..53cdc8548c 100644 --- a/go/apps/api/routes/v2_apis_list_keys/200_test.go +++ b/go/apps/api/routes/v2_apis_list_keys/200_test.go @@ -556,6 +556,7 @@ func TestSuccess(t *testing.T) { } require.NotEmpty(t, key.Plaintext, "Key should be decrypted and have plaintext") + require.Equal(t, expectedPlaintext, key.Plaintext, "Key should be decrypted and have correct plaintext") } }) } From 7cf3083eeb683408c9db5cf21a54df0d53f03ccc Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 14:08:14 +0100 Subject: [PATCH 8/9] fix: less check --- go/apps/api/routes/v2_apis_list_keys/handler.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/go/apps/api/routes/v2_apis_list_keys/handler.go b/go/apps/api/routes/v2_apis_list_keys/handler.go index e59314e636..71275b88a7 100644 --- a/go/apps/api/routes/v2_apis_list_keys/handler.go +++ b/go/apps/api/routes/v2_apis_list_keys/handler.go @@ -163,7 +163,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { cursor := ptr.SafeDeref(req.Cursor, "") // Resolve identity ID if external_id filter is provided - var identityID string + var identityID sql.NullString if req.ExternalId != nil && *req.ExternalId != "" { identity, identityErr := db.Query.FindIdentityByExternalID(ctx, h.DB.RO(), db.FindIdentityByExternalIDParams{ WorkspaceID: auth.AuthorizedWorkspaceID, @@ -190,22 +190,17 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { fault.Public("Failed to retrieve identity."), ) } - identityID = identity.ID + identityID = sql.NullString{String: identity.ID, Valid: true} } // Query keys by key_auth_id instead of api_id - var identityIDParam sql.NullString - if identityID != "" { - identityIDParam = sql.NullString{String: identityID, Valid: true} - } - keyResults, err := db.Query.ListLiveKeysByKeySpaceID( ctx, h.DB.RO(), db.ListLiveKeysByKeySpaceIDParams{ KeySpaceID: api.KeyAuthID.String, IDCursor: cursor, - IdentityID: identityIDParam, + IdentityID: identityID, Limit: int32(limit + 1), // nolint:gosec }, ) From 3d18e1d209949cc288c98a05200f2da25a552f73 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 10 Nov 2025 14:36:36 +0100 Subject: [PATCH 9/9] remove unneded join --- go/pkg/db/key_list_live_by_key_space_id.sql_generated.go | 4 ---- go/pkg/db/querier_generated.go | 2 -- go/pkg/db/queries/key_list_live_by_key_space_id.sql | 2 -- 3 files changed, 8 deletions(-) diff --git a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go index 935332f804..4b4522f825 100644 --- a/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go +++ b/go/pkg/db/key_list_live_by_key_space_id.sql_generated.go @@ -91,7 +91,6 @@ SELECT k.id, k.key_auth_id, k.hash, k.start, k.workspace_id, k.for_workspace_id, ) AS ratelimits FROM ` + "`" + `keys` + "`" + ` k STRAIGHT_JOIN key_auth ka ON ka.id = k.key_auth_id - STRAIGHT_JOIN workspaces ws ON ws.id = k.workspace_id LEFT JOIN identities i ON k.identity_id = i.id AND i.deleted = false LEFT JOIN encrypted_keys ek ON ek.key_id = k.id WHERE k.key_auth_id = ? @@ -99,7 +98,6 @@ WHERE k.key_auth_id = ? AND (? IS NULL OR k.identity_id = ?) AND k.deleted_at_m IS NULL AND ka.deleted_at_m IS NULL - AND ws.deleted_at_m IS NULL ORDER BY k.id ASC LIMIT ? ` @@ -228,7 +226,6 @@ type ListLiveKeysByKeySpaceIDRow struct { // ) AS ratelimits // FROM `keys` k // STRAIGHT_JOIN key_auth ka ON ka.id = k.key_auth_id -// STRAIGHT_JOIN workspaces ws ON ws.id = k.workspace_id // LEFT JOIN identities i ON k.identity_id = i.id AND i.deleted = false // LEFT JOIN encrypted_keys ek ON ek.key_id = k.id // WHERE k.key_auth_id = ? @@ -236,7 +233,6 @@ type ListLiveKeysByKeySpaceIDRow struct { // AND (? IS NULL OR k.identity_id = ?) // AND k.deleted_at_m IS NULL // AND ka.deleted_at_m IS NULL -// AND ws.deleted_at_m IS NULL // ORDER BY k.id ASC // LIMIT ? func (q *Queries) ListLiveKeysByKeySpaceID(ctx context.Context, db DBTX, arg ListLiveKeysByKeySpaceIDParams) ([]ListLiveKeysByKeySpaceIDRow, error) { diff --git a/go/pkg/db/querier_generated.go b/go/pkg/db/querier_generated.go index 06608a447d..bbc1d0170b 100644 --- a/go/pkg/db/querier_generated.go +++ b/go/pkg/db/querier_generated.go @@ -1569,7 +1569,6 @@ type Querier interface { // ) AS ratelimits // FROM `keys` k // STRAIGHT_JOIN key_auth ka ON ka.id = k.key_auth_id - // STRAIGHT_JOIN workspaces ws ON ws.id = k.workspace_id // LEFT JOIN identities i ON k.identity_id = i.id AND i.deleted = false // LEFT JOIN encrypted_keys ek ON ek.key_id = k.id // WHERE k.key_auth_id = ? @@ -1577,7 +1576,6 @@ type Querier interface { // AND (? IS NULL OR k.identity_id = ?) // AND k.deleted_at_m IS NULL // AND ka.deleted_at_m IS NULL - // AND ws.deleted_at_m IS NULL // ORDER BY k.id ASC // LIMIT ? ListLiveKeysByKeySpaceID(ctx context.Context, db DBTX, arg ListLiveKeysByKeySpaceIDParams) ([]ListLiveKeysByKeySpaceIDRow, error) diff --git a/go/pkg/db/queries/key_list_live_by_key_space_id.sql b/go/pkg/db/queries/key_list_live_by_key_space_id.sql index 7c876d791d..86f48998cd 100644 --- a/go/pkg/db/queries/key_list_live_by_key_space_id.sql +++ b/go/pkg/db/queries/key_list_live_by_key_space_id.sql @@ -79,7 +79,6 @@ SELECT k.*, ) AS ratelimits FROM `keys` k STRAIGHT_JOIN key_auth ka ON ka.id = k.key_auth_id - STRAIGHT_JOIN workspaces ws ON ws.id = k.workspace_id LEFT JOIN identities i ON k.identity_id = i.id AND i.deleted = false LEFT JOIN encrypted_keys ek ON ek.key_id = k.id WHERE k.key_auth_id = sqlc.arg(key_space_id) @@ -87,6 +86,5 @@ WHERE k.key_auth_id = sqlc.arg(key_space_id) AND (sqlc.arg(identity_id) IS NULL OR k.identity_id = sqlc.arg(identity_id)) AND k.deleted_at_m IS NULL AND ka.deleted_at_m IS NULL - AND ws.deleted_at_m IS NULL ORDER BY k.id ASC LIMIT ?;