diff --git a/go/apps/api/routes/v2_apis_create_api/handler.go b/go/apps/api/routes/v2_apis_create_api/handler.go index c49c084753..ef4f823884 100644 --- a/go/apps/api/routes/v2_apis_create_api/handler.go +++ b/go/apps/api/routes/v2_apis_create_api/handler.go @@ -39,7 +39,8 @@ func (h *Handler) Path() string { } func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -49,7 +50,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_apis_delete_api/handler.go b/go/apps/api/routes/v2_apis_delete_api/handler.go index 8075053e69..6c16c82027 100644 --- a/go/apps/api/routes/v2_apis_delete_api/handler.go +++ b/go/apps/api/routes/v2_apis_delete_api/handler.go @@ -45,7 +45,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -54,7 +55,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { if err != nil { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_apis_get_api/handler.go b/go/apps/api/routes/v2_apis_get_api/handler.go index d294a4be4c..10aaaae536 100644 --- a/go/apps/api/routes/v2_apis_get_api/handler.go +++ b/go/apps/api/routes/v2_apis_get_api/handler.go @@ -38,7 +38,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/apis.getApi") - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -48,7 +49,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", 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 1d97c23e9b..2497683c7a 100644 --- a/go/apps/api/routes/v2_apis_list_keys/handler.go +++ b/go/apps/api/routes/v2_apis_list_keys/handler.go @@ -45,7 +45,8 @@ func (h *Handler) Path() string { // The current implementation queries the database directly without caching, which may impact performance. // Consider implementing cache with optional bypass via revalidateKeysCache parameter. func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -54,7 +55,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { if err != nil { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.And( rbac.Or( rbac.T(rbac.Tuple{ @@ -147,7 +148,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_identities_create_identity/handler.go b/go/apps/api/routes/v2_identities_create_identity/handler.go index 090ec17e97..3c042cb133 100644 --- a/go/apps/api/routes/v2_identities_create_identity/handler.go +++ b/go/apps/api/routes/v2_identities_create_identity/handler.go @@ -51,7 +51,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -61,7 +62,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Identity, ResourceID: "*", 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 02d2ff23b8..e897ecd487 100644 --- a/go/apps/api/routes/v2_identities_delete_identity/handler.go +++ b/go/apps/api/routes/v2_identities_delete_identity/handler.go @@ -42,7 +42,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -53,7 +54,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions( + err = auth.VerifyRootKey(ctx, keys.WithPermissions( rbac.Or( rbac.T( rbac.Tuple{ 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 0c05029def..98f9f9aba2 100644 --- a/go/apps/api/routes/v2_identities_get_identity/handler.go +++ b/go/apps/api/routes/v2_identities_get_identity/handler.go @@ -41,7 +41,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -100,7 +101,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ratelimits := result.Ratelimits // Check permissions using either wildcard or the specific identity ID - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Identity, ResourceID: "*", diff --git a/go/apps/api/routes/v2_identities_list_identities/handler.go b/go/apps/api/routes/v2_identities_list_identities/handler.go index c4ac367978..59ed3e0991 100644 --- a/go/apps/api/routes/v2_identities_list_identities/handler.go +++ b/go/apps/api/routes/v2_identities_list_identities/handler.go @@ -40,7 +40,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -93,7 +94,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { }), ) - err = auth.Verify(ctx, keys.WithPermissions(permissionCheck)) + err = auth.VerifyRootKey(ctx, keys.WithPermissions(permissionCheck)) if err != nil { return err } 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 1caf73984b..457d397486 100644 --- a/go/apps/api/routes/v2_identities_update_identity/handler.go +++ b/go/apps/api/routes/v2_identities_update_identity/handler.go @@ -50,7 +50,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -61,7 +62,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Identity, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_add_permissions/handler.go b/go/apps/api/routes/v2_keys_add_permissions/handler.go index 0b02fff491..5bb1e05f3b 100644 --- a/go/apps/api/routes/v2_keys_add_permissions/handler.go +++ b/go/apps/api/routes/v2_keys_add_permissions/handler.go @@ -45,7 +45,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -55,6 +56,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } + // 4. Validate key exists and belongs to workspace key, err := db.Query.FindKeyByIdOrHash(ctx, h.DB.RO(), db.FindKeyByIdOrHashParams{ @@ -66,23 +68,27 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { if db.IsNotFound(err) { return fault.New("key not found", fault.Code(codes.Data.Key.NotFound.URN()), - fault.Internal("key not found"), fault.Public("The specified key was not found."), + fault.Internal("key not found"), + fault.Public("The specified key was not found."), ) } + return fault.Wrap(err, fault.Code(codes.App.Internal.ServiceUnavailable.URN()), - fault.Internal("database error"), fault.Public("Failed to retrieve key."), + fault.Internal("database error"), + fault.Public("Failed to retrieve key."), ) } if key.WorkspaceID != auth.AuthorizedWorkspaceID { return fault.New("key not found", fault.Code(codes.Data.Key.NotFound.URN()), - fault.Internal("key belongs to different workspace"), fault.Public("The specified key was not found."), + fault.Internal("key belongs to different workspace"), + fault.Public("The specified key was not found."), ) } - err = auth.Verify(ctx, keys.WithPermissions( + err = auth.VerifyRootKey(ctx, keys.WithPermissions( rbac.And( rbac.Or( rbac.T(rbac.Tuple{ @@ -156,7 +162,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } for perm := range missingPermissions { - err = auth.Verify(ctx, keys.WithPermissions( + err = auth.VerifyRootKey(ctx, keys.WithPermissions( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_add_roles/handler.go b/go/apps/api/routes/v2_keys_add_roles/handler.go index f1c39fef37..d6b0654b5d 100644 --- a/go/apps/api/routes/v2_keys_add_roles/handler.go +++ b/go/apps/api/routes/v2_keys_add_roles/handler.go @@ -46,7 +46,8 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.addRoles") - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -84,7 +85,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions( + err = auth.VerifyRootKey(ctx, keys.WithPermissions( rbac.And( rbac.Or( rbac.T(rbac.Tuple{ diff --git a/go/apps/api/routes/v2_keys_create_key/handler.go b/go/apps/api/routes/v2_keys_create_key/handler.go index 24b5157e1b..ce16b0b7cf 100644 --- a/go/apps/api/routes/v2_keys_create_key/handler.go +++ b/go/apps/api/routes/v2_keys_create_key/handler.go @@ -53,7 +53,8 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.createKey") // 1. Authentication - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -65,7 +66,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // 3. Permission check - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: req.ApiId, @@ -138,7 +139,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_delete_key/handler.go b/go/apps/api/routes/v2_keys_delete_key/handler.go index 2e9a5418ce..9b513039cb 100644 --- a/go/apps/api/routes/v2_keys_delete_key/handler.go +++ b/go/apps/api/routes/v2_keys_delete_key/handler.go @@ -47,7 +47,8 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.deleteKey") // Authentication - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -92,7 +93,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // Permission check - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_get_key/handler.go b/go/apps/api/routes/v2_keys_get_key/handler.go index f86ea69367..cd55e669f4 100644 --- a/go/apps/api/routes/v2_keys_get_key/handler.go +++ b/go/apps/api/routes/v2_keys_get_key/handler.go @@ -48,7 +48,8 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.getKey") // Authentication - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -92,7 +93,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // Permission check - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", @@ -133,7 +134,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // Permission check - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_remove_permissions/handler.go b/go/apps/api/routes/v2_keys_remove_permissions/handler.go index ab918d8e6d..3ea23294cc 100644 --- a/go/apps/api/routes/v2_keys_remove_permissions/handler.go +++ b/go/apps/api/routes/v2_keys_remove_permissions/handler.go @@ -46,7 +46,9 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.removePermissions") - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -56,6 +58,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } + // 4. Validate key exists and belongs to workspace key, err := db.Query.FindKeyByIdOrHash(ctx, h.DB.RO(), db.FindKeyByIdOrHashParams{ @@ -85,7 +88,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions( + err = auth.VerifyRootKey(ctx, keys.WithPermissions( rbac.And( rbac.Or( rbac.T(rbac.Tuple{ diff --git a/go/apps/api/routes/v2_keys_remove_roles/handler.go b/go/apps/api/routes/v2_keys_remove_roles/handler.go index 90d9c36283..2f1b610dbc 100644 --- a/go/apps/api/routes/v2_keys_remove_roles/handler.go +++ b/go/apps/api/routes/v2_keys_remove_roles/handler.go @@ -46,7 +46,9 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.removeRoles") - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -83,7 +85,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_set_permissions/handler.go b/go/apps/api/routes/v2_keys_set_permissions/handler.go index 9d3e911551..db34ca1c96 100644 --- a/go/apps/api/routes/v2_keys_set_permissions/handler.go +++ b/go/apps/api/routes/v2_keys_set_permissions/handler.go @@ -48,7 +48,9 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.setPermissions") - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -58,6 +60,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } + // 4. Validate key exists and belongs to workspace key, err := db.Query.FindKeyByIdOrHash(ctx, h.DB.RO(), db.FindKeyByIdOrHashParams{ @@ -85,7 +88,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions( + err = auth.VerifyRootKey(ctx, keys.WithPermissions( rbac.And( rbac.Or( rbac.T(rbac.Tuple{ @@ -153,8 +156,8 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { permissionsToSet = append(permissionsToSet, permission) } - for perm := range missingPermissions { - err = auth.Verify(ctx, keys.WithPermissions( + if len(missingPermissions) > 0 { + err = auth.VerifyRootKey(ctx, keys.WithPermissions( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", @@ -164,7 +167,9 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { if err != nil { return err } + } + for perm := range missingPermissions { permissionID := uid.New(uid.PermissionPrefix) now := time.Now().UnixMilli() permissionsToInsert = append(permissionsToInsert, db.InsertPermissionParams{ diff --git a/go/apps/api/routes/v2_keys_set_roles/handler.go b/go/apps/api/routes/v2_keys_set_roles/handler.go index 082b56c7f8..7e110a4fe3 100644 --- a/go/apps/api/routes/v2_keys_set_roles/handler.go +++ b/go/apps/api/routes/v2_keys_set_roles/handler.go @@ -47,7 +47,8 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.setRoles") - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -85,7 +86,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_update_credits/handler.go b/go/apps/api/routes/v2_keys_update_credits/handler.go index b60cd9568a..a4f427b3f5 100644 --- a/go/apps/api/routes/v2_keys_update_credits/handler.go +++ b/go/apps/api/routes/v2_keys_update_credits/handler.go @@ -47,7 +47,8 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.updateCredits") // Authentication - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -92,7 +93,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // Permission check - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_update_key/handler.go b/go/apps/api/routes/v2_keys_update_key/handler.go index f0fa5174e1..3ad7852e1b 100644 --- a/go/apps/api/routes/v2_keys_update_key/handler.go +++ b/go/apps/api/routes/v2_keys_update_key/handler.go @@ -47,7 +47,8 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.updateKey") - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -90,7 +91,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // TODO: We should actually check if the user has permission to set/remove roles. - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_keys_verify_key/handler.go b/go/apps/api/routes/v2_keys_verify_key/handler.go index b6933e8462..a8ebe4bcf0 100644 --- a/go/apps/api/routes/v2_keys_verify_key/handler.go +++ b/go/apps/api/routes/v2_keys_verify_key/handler.go @@ -49,7 +49,8 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/keys.verifyKey") // Authentication - auth, err := h.Keys.GetRootKey(ctx, s) + auth, rootEmit, err := h.Keys.GetRootKey(ctx, s) + defer rootEmit() if err != nil { return err } @@ -60,7 +61,8 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - key, err := h.Keys.Get(ctx, s, req.Key) + key, emit, err := h.Keys.Get(ctx, s, req.Key) + defer emit() if err != nil { return err } @@ -93,7 +95,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { }) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", @@ -120,7 +122,10 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { }) } - opts := []keys.VerifyOption{keys.WithIPWhitelist(), keys.WithTags(ptr.SafeDeref(req.Tags))} + opts := []keys.VerifyOption{ + keys.WithTags(ptr.SafeDeref(req.Tags)), + keys.WithIPWhitelist(), + } // If a custom cost was specified, use it, otherwise use a DefaultCost of 1 if req.Credits != nil { diff --git a/go/apps/api/routes/v2_keys_whoami/handler.go b/go/apps/api/routes/v2_keys_whoami/handler.go index 181f8eb1be..bf881e3dcb 100644 --- a/go/apps/api/routes/v2_keys_whoami/handler.go +++ b/go/apps/api/routes/v2_keys_whoami/handler.go @@ -45,8 +45,8 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - // Authentication - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -90,7 +90,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // Permission check - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Api, ResourceID: "*", diff --git a/go/apps/api/routes/v2_permissions_create_permission/handler.go b/go/apps/api/routes/v2_permissions_create_permission/handler.go index e12ddb047c..a1dc138f81 100644 --- a/go/apps/api/routes/v2_permissions_create_permission/handler.go +++ b/go/apps/api/routes/v2_permissions_create_permission/handler.go @@ -44,7 +44,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -54,7 +55,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.T(rbac.Tuple{ + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", Action: rbac.CreatePermission, diff --git a/go/apps/api/routes/v2_permissions_create_role/handler.go b/go/apps/api/routes/v2_permissions_create_role/handler.go index a69988bb34..cabbe96006 100644 --- a/go/apps/api/routes/v2_permissions_create_role/handler.go +++ b/go/apps/api/routes/v2_permissions_create_role/handler.go @@ -48,7 +48,8 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/permissions.createRole") // 1. Authentication - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -60,7 +61,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // 3. Permission check - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", diff --git a/go/apps/api/routes/v2_permissions_delete_permission/handler.go b/go/apps/api/routes/v2_permissions_delete_permission/handler.go index 55a902fbb6..676b1ad32b 100644 --- a/go/apps/api/routes/v2_permissions_delete_permission/handler.go +++ b/go/apps/api/routes/v2_permissions_delete_permission/handler.go @@ -40,7 +40,9 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -50,7 +52,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", diff --git a/go/apps/api/routes/v2_permissions_delete_role/handler.go b/go/apps/api/routes/v2_permissions_delete_role/handler.go index 954b5be8d1..955ae5735d 100644 --- a/go/apps/api/routes/v2_permissions_delete_role/handler.go +++ b/go/apps/api/routes/v2_permissions_delete_role/handler.go @@ -42,7 +42,9 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/permissions.deleteRole") - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -52,7 +54,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", diff --git a/go/apps/api/routes/v2_permissions_get_permission/handler.go b/go/apps/api/routes/v2_permissions_get_permission/handler.go index 774a3610fd..bafcf9a032 100644 --- a/go/apps/api/routes/v2_permissions_get_permission/handler.go +++ b/go/apps/api/routes/v2_permissions_get_permission/handler.go @@ -39,7 +39,9 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/permissions.getPermission") - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -49,7 +51,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", diff --git a/go/apps/api/routes/v2_permissions_get_role/handler.go b/go/apps/api/routes/v2_permissions_get_role/handler.go index a9cf7d0959..b02300fbaf 100644 --- a/go/apps/api/routes/v2_permissions_get_role/handler.go +++ b/go/apps/api/routes/v2_permissions_get_role/handler.go @@ -40,7 +40,9 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/permissions.getRole") - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -50,7 +52,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { return err } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", diff --git a/go/apps/api/routes/v2_permissions_list_permissions/handler.go b/go/apps/api/routes/v2_permissions_list_permissions/handler.go index 29a74cfe1b..7148b78e54 100644 --- a/go/apps/api/routes/v2_permissions_list_permissions/handler.go +++ b/go/apps/api/routes/v2_permissions_list_permissions/handler.go @@ -39,7 +39,9 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/permissions.listPermissions") - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -52,7 +54,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { cursor := ptr.SafeDeref(req.Cursor, "") - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", diff --git a/go/apps/api/routes/v2_permissions_list_roles/handler.go b/go/apps/api/routes/v2_permissions_list_roles/handler.go index 56c966c830..917debd1ac 100644 --- a/go/apps/api/routes/v2_permissions_list_roles/handler.go +++ b/go/apps/api/routes/v2_permissions_list_roles/handler.go @@ -41,7 +41,9 @@ func (h *Handler) Path() string { func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { h.Logger.Debug("handling request", "requestId", s.RequestID(), "path", "/v2/permissions.listRoles") - auth, err := h.Keys.GetRootKey(ctx, s) + // 1. Authentication + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -53,7 +55,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { cursor := ptr.SafeDeref(req.Cursor, "") - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Rbac, ResourceID: "*", diff --git a/go/apps/api/routes/v2_ratelimit_delete_override/handler.go b/go/apps/api/routes/v2_ratelimit_delete_override/handler.go index c678d5297a..e507543d35 100644 --- a/go/apps/api/routes/v2_ratelimit_delete_override/handler.go +++ b/go/apps/api/routes/v2_ratelimit_delete_override/handler.go @@ -44,7 +44,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -77,7 +78,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Ratelimit, ResourceID: namespace.ID, diff --git a/go/apps/api/routes/v2_ratelimit_get_override/handler.go b/go/apps/api/routes/v2_ratelimit_get_override/handler.go index ed6c45ee95..2c29652e91 100644 --- a/go/apps/api/routes/v2_ratelimit_get_override/handler.go +++ b/go/apps/api/routes/v2_ratelimit_get_override/handler.go @@ -43,7 +43,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -116,7 +117,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Ratelimit, ResourceID: namespace.ID, diff --git a/go/apps/api/routes/v2_ratelimit_limit/handler.go b/go/apps/api/routes/v2_ratelimit_limit/handler.go index c0d6e242bd..7fd84d0f7a 100644 --- a/go/apps/api/routes/v2_ratelimit_limit/handler.go +++ b/go/apps/api/routes/v2_ratelimit_limit/handler.go @@ -52,7 +52,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { // Authenticate the request with a root key - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -138,7 +139,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { } // Verify permissions for rate limiting - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Ratelimit, ResourceID: namespace.ID, diff --git a/go/apps/api/routes/v2_ratelimit_list_overrides/handler.go b/go/apps/api/routes/v2_ratelimit_list_overrides/handler.go index 6c925bed16..08b8aa33cb 100644 --- a/go/apps/api/routes/v2_ratelimit_list_overrides/handler.go +++ b/go/apps/api/routes/v2_ratelimit_list_overrides/handler.go @@ -37,7 +37,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -81,7 +82,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Ratelimit, ResourceID: namespace.ID, diff --git a/go/apps/api/routes/v2_ratelimit_set_override/handler.go b/go/apps/api/routes/v2_ratelimit_set_override/handler.go index 133432ad08..2a1905a8eb 100644 --- a/go/apps/api/routes/v2_ratelimit_set_override/handler.go +++ b/go/apps/api/routes/v2_ratelimit_set_override/handler.go @@ -46,7 +46,8 @@ func (h *Handler) Path() string { // Handle processes the HTTP request func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { - auth, err := h.Keys.GetRootKey(ctx, s) + auth, emit, err := h.Keys.GetRootKey(ctx, s) + defer emit() if err != nil { return err } @@ -78,7 +79,7 @@ func (h *Handler) Handle(ctx context.Context, s *zen.Session) error { ) } - err = auth.Verify(ctx, keys.WithPermissions(rbac.Or( + err = auth.VerifyRootKey(ctx, keys.WithPermissions(rbac.Or( rbac.T(rbac.Tuple{ ResourceType: rbac.Ratelimit, ResourceID: namespace.ID, diff --git a/go/internal/services/keys/get.go b/go/internal/services/keys/get.go index 92bc1ec0b2..fbe613b911 100644 --- a/go/internal/services/keys/get.go +++ b/go/internal/services/keys/get.go @@ -19,45 +19,50 @@ import ( // GetRootKey retrieves and validates a root key from the session's Authorization header. // Root keys are special administrative keys that can access workspace-level operations. // Validation failures are immediately converted to fault errors for root keys. -func (s *service) GetRootKey(ctx context.Context, sess *zen.Session) (*KeyVerifier, error) { +func (s *service) GetRootKey(ctx context.Context, sess *zen.Session) (*KeyVerifier, func(), error) { ctx, span := tracing.Start(ctx, "keys.GetRootKey") defer span.End() rootKey, err := zen.Bearer(sess) if err != nil { - return nil, fault.Wrap(err, + return nil, emptyLog, fault.Wrap(err, fault.Internal("no bearer"), fault.Public("You must provide a valid root key in the Authorization header in the format 'Bearer ROOT_KEY'."), ) } - key, err := s.Get(ctx, sess, rootKey) + key, log, err := s.Get(ctx, sess, rootKey) + if key.Key.ForWorkspaceID.Valid { + key.AuthorizedWorkspaceID = key.Key.ForWorkspaceID.String + } + sess.WorkspaceID = key.AuthorizedWorkspaceID if err != nil { - return nil, err + return nil, log, err } - // For root keys, convert validation failures to proper fault errors immediately if key.Status != StatusValid { - return nil, fault.Wrap( + return nil, log, fault.Wrap( key.ToFault(), fault.Internal("invalid root key"), fault.Public("The provided root key is invalid."), ) } - return key, nil + return key, log, nil } +var emptyLog = func() {} + // Get retrieves a key from the database and performs basic validation checks. // It returns a KeyVerifier that can be used for further validation with specific options. // For normal keys, validation failures are indicated by KeyVerifier.Valid=false. -func (s *service) Get(ctx context.Context, sess *zen.Session, rawKey string) (*KeyVerifier, error) { +func (s *service) Get(ctx context.Context, sess *zen.Session, rawKey string) (*KeyVerifier, func(), error) { ctx, span := tracing.Start(ctx, "keys.Get") defer span.End() err := assert.NotEmpty(rawKey) if err != nil { - return nil, fault.Wrap(err, fault.Internal("rawKey is empty")) + return nil, emptyLog, fault.Wrap(err, fault.Internal("rawKey is empty")) } h := hash.Sha256(rawKey) @@ -70,10 +75,10 @@ func (s *service) Get(ctx context.Context, sess *zen.Session, rawKey string) (*K return &KeyVerifier{ Status: StatusNotFound, message: "key does not exist", - }, nil + }, emptyLog, nil } - return nil, fault.Wrap( + return nil, emptyLog, fault.Wrap( err, fault.Internal("unable to load key"), fault.Public("We could not load the requested key."), @@ -85,7 +90,7 @@ func (s *service) Get(ctx context.Context, sess *zen.Session, rawKey string) (*K return &KeyVerifier{ Status: StatusNotFound, message: "key does not exist", - }, nil + }, nil, nil } // ForWorkspace set but that doesn't exist @@ -94,15 +99,17 @@ func (s *service) Get(ctx context.Context, sess *zen.Session, rawKey string) (*K return &KeyVerifier{ Status: StatusWorkspaceNotFound, message: "workspace not found", - }, nil + }, emptyLog, nil + } + + kv := &KeyVerifier{ + Status: StatusWorkspaceDisabled, + message: "workspace is disabled", } if !key.WorkspaceEnabled || (key.ForWorkspaceEnabled.Valid && !key.ForWorkspaceEnabled.Bool) { // nolint:exhaustruct - return &KeyVerifier{ - Status: StatusWorkspaceDisabled, - message: "workspace is disabled", - }, nil + return kv, kv.log, nil } // The DB returns this in array format and an empty array if not found @@ -110,15 +117,15 @@ func (s *service) Get(ctx context.Context, sess *zen.Session, rawKey string) (*K var ratelimitArr []db.KeyFindForVerificationRatelimit err = json.Unmarshal(key.Roles.([]byte), &roles) if err != nil { - return nil, err + return nil, emptyLog, err } err = json.Unmarshal(key.Permissions.([]byte), &permissions) if err != nil { - return nil, err + return nil, emptyLog, err } err = json.Unmarshal(key.Ratelimits.([]byte), &ratelimitArr) if err != nil { - return nil, err + return nil, emptyLog, err } // Convert rate limits array to map (key name -> config) @@ -136,18 +143,12 @@ func (s *service) Get(ctx context.Context, sess *zen.Session, rawKey string) (*K } } - authorizedWorkspaceID := key.WorkspaceID - if key.ForWorkspaceID.Valid { - authorizedWorkspaceID = key.ForWorkspaceID.String - } - - sess.WorkspaceID = authorizedWorkspaceID - kv := &KeyVerifier{ + kv = &KeyVerifier{ Key: key, clickhouse: s.clickhouse, rateLimiter: s.raterLimiter, usageLimiter: s.usageLimiter, - AuthorizedWorkspaceID: authorizedWorkspaceID, + AuthorizedWorkspaceID: key.WorkspaceID, rBAC: s.rbac, session: sess, logger: s.logger, @@ -164,23 +165,23 @@ func (s *service) Get(ctx context.Context, sess *zen.Session, rawKey string) (*K if key.DeletedAtM.Valid { kv.setInvalid(StatusNotFound, "key is deleted") - return kv, nil + return kv, kv.log, nil } if key.ApiDeletedAtM.Valid { kv.setInvalid(StatusNotFound, "key is deleted") - return kv, nil + return kv, kv.log, nil } if !key.Enabled { kv.setInvalid(StatusDisabled, "key is disabled") - return kv, nil + return kv, kv.log, nil } if key.Expires.Valid && time.Now().After(key.Expires.Time) { kv.setInvalid(StatusExpired, fmt.Sprintf("the key has expired on %s", key.Expires.Time.Format(time.RFC3339))) - return kv, nil + return kv, kv.log, nil } - return kv, nil + return kv, kv.log, nil } diff --git a/go/internal/services/keys/interface.go b/go/internal/services/keys/interface.go index a1006af80c..88302f4041 100644 --- a/go/internal/services/keys/interface.go +++ b/go/internal/services/keys/interface.go @@ -10,9 +10,9 @@ import ( // It provides methods for key creation, retrieval, and validation. type KeyService interface { // Get retrieves a key and returns a KeyVerifier for validation - Get(ctx context.Context, sess *zen.Session, hash string) (*KeyVerifier, error) + Get(ctx context.Context, sess *zen.Session, hash string) (*KeyVerifier, func(), error) // GetRootKey retrieves and validates a root key from the session - GetRootKey(ctx context.Context, sess *zen.Session) (*KeyVerifier, error) + GetRootKey(ctx context.Context, sess *zen.Session) (*KeyVerifier, func(), error) // CreateKey generates a new secure API key CreateKey(ctx context.Context, req CreateKeyRequest) (CreateKeyResponse, error) } diff --git a/go/internal/services/keys/options.go b/go/internal/services/keys/options.go index a862468a1f..4f12d9f09f 100644 --- a/go/internal/services/keys/options.go +++ b/go/internal/services/keys/options.go @@ -27,6 +27,7 @@ func WithCredits(cost int32) VerifyOption { if cost < 0 { return errors.New("cost cannot be negative") } + config.credits = &cost return nil } diff --git a/go/internal/services/keys/validation.go b/go/internal/services/keys/validation.go index 33d629b742..ab62a8fb32 100644 --- a/go/internal/services/keys/validation.go +++ b/go/internal/services/keys/validation.go @@ -97,16 +97,6 @@ func (k *KeyVerifier) withIPWhitelist() error { return nil } -func (k *KeyVerifier) WithApiID(apiID string) { - if k.Status != StatusValid { - return - } - - if k.Key.ApiID != apiID { - k.setInvalid(StatusForbidden, fmt.Sprintf("The key does not belong to %s", apiID)) - } -} - // withPermissions validates that the key has the required RBAC permissions. // It uses the configured RBAC system to evaluate the permission query against the key's permissions. func (k *KeyVerifier) withPermissions(ctx context.Context, query rbac.PermissionQuery) error { @@ -223,7 +213,14 @@ func (k *KeyVerifier) withRateLimits(ctx context.Context, specifiedLimits []open Time: time.Now(), }) if err != nil { - return err + k.logger.Error("Failed to ratelimit", + "key_id", k.Key.ID, + "Identifier", config.Identifier, + "error", err.Error(), + ) + + // We will just allow the request to proceed, but log the error + return nil } config.Response = &response diff --git a/go/internal/services/keys/verifier.go b/go/internal/services/keys/verifier.go index c71bb6d9a9..fa778eebdc 100644 --- a/go/internal/services/keys/verifier.go +++ b/go/internal/services/keys/verifier.go @@ -44,6 +44,7 @@ type KeyVerifier struct { clickhouse clickhouse.ClickHouse // Clickhouse for telemetry logger logging.Logger // Logger for verification operations message string // Internal message for validation failures + tags []string // Tags associated with the key } // GetRatelimitConfigs returns the rate limit configurations @@ -51,16 +52,21 @@ func (k *KeyVerifier) GetRatelimitConfigs() map[string]db.KeyFindForVerification return k.ratelimitConfigs } +func (k *KeyVerifier) VerifyRootKey(ctx context.Context, opts ...VerifyOption) error { + err := k.Verify(ctx, opts...) + if err != nil { + return err + } + + return k.ToFault() +} + // Verify performs key verification with the given options. // For root keys: returns fault errors for validation failures. // For normal keys: returns error only for system problems, check k.Valid and k.Status for validation results. func (k *KeyVerifier) Verify(ctx context.Context, opts ...VerifyOption) error { // Skip verification if key is already invalid if k.Status != StatusValid { - // For root keys, auto-return validation failures as fault errors - if k.isRootKey { - return k.ToFault() - } return nil } @@ -72,14 +78,11 @@ func (k *KeyVerifier) Verify(ctx context.Context, opts ...VerifyOption) error { } } - var err error - if config.credits != nil { - err = k.withCredits(ctx, *config.credits) - if err != nil { - return err - } + if config.tags != nil { + k.tags = config.tags } + var err error if config.ipWhitelist { err = k.withIPWhitelist() if err != nil { @@ -99,6 +102,17 @@ func (k *KeyVerifier) Verify(ctx context.Context, opts ...VerifyOption) error { return err } + if config.credits != nil { + err = k.withCredits(ctx, *config.credits) + if err != nil { + return err + } + } + + return nil +} + +func (k *KeyVerifier) log() { k.clickhouse.BufferKeyVerification(schema.KeyVerificationRequestV1{ RequestID: k.session.RequestID(), WorkspaceID: k.session.AuthorizedWorkspaceID(), @@ -108,23 +122,17 @@ func (k *KeyVerifier) Verify(ctx context.Context, opts ...VerifyOption) error { KeySpaceID: k.Key.KeyAuthID, KeyID: k.Key.ID, IdentityID: k.Key.IdentityID.String, - Tags: config.tags, + Tags: k.tags, }) keyType := "key" if k.isRootKey { keyType = "root_key" } + // Emit Prometheus metrics for key verification metrics.KeyVerificationsTotal.WithLabelValues( keyType, string(k.Status), // code ).Inc() - - // For root keys, auto-return validation failures as fault errors - if k.isRootKey && k.Status != StatusValid { - return k.ToFault() - } - - return nil }