diff --git a/core/services/addons/gateway/credentials.go b/core/services/addons/gateway/credentials.go index fe21e24bda..b9d1d530f7 100644 --- a/core/services/addons/gateway/credentials.go +++ b/core/services/addons/gateway/credentials.go @@ -114,8 +114,9 @@ func (g *Gateway) credentialize(ctx context.Context, a contract.Actor, r *contra caps, err := src.DeclaredCapabilities(ctx, r.AddonID, r.Generation) if err != nil { // Not knowing whether the add-on declares credentials is not a reason - // to run its tool without the check. - return ErrUnavailable + // to run its tool without the check. A stale generation is a denial, + // not an outage of the owner. + return toolSourceError(err) } if len(caps.Credentials) == 0 { return nil diff --git a/core/services/addons/gateway/credentials_test.go b/core/services/addons/gateway/credentials_test.go index bd1b3c32e6..c423fd600f 100644 --- a/core/services/addons/gateway/credentials_test.go +++ b/core/services/addons/gateway/credentials_test.go @@ -136,6 +136,15 @@ var _ = Describe("a read of an add-on that declares credentials", Label("tools") Expect(err).To(MatchError(ErrUnavailable)) }) + It("denies a stale generation as a denial, never as an outage of the owner", func() { + g.Resolver = resolved{} + _, err := g.Dispatch(ctx, contract.Actor{UserID: "alice", CredentialID: "c", Mode: "named-user"}, + contract.Request{AddonID: "sample", ResourceID: "r-0123456789", Generation: 7, OperationID: "op", Action: "read", Arguments: json.RawMessage(`{"q":"plain"}`)}) + Expect(err).To(MatchError(ErrForbidden)) + Expect(err).NotTo(MatchError(ErrUnavailable)) + Expect(client.reads.Load()).To(Equal(int32(0))) + }) + It("does not touch an add-on that declares no credentials", func() { plain := capSet{toolSet: toolSet{gen: 1, tools: map[string][]string{"sample": {"whoami"}}}, defs: defs[1:]} g.Tools = plain