github.com/storacha/go-ucanto@v0.7.2/server/handler.go (about) 1 package server 2 3 import ( 4 "context" 5 6 "github.com/storacha/go-ucanto/core/invocation" 7 "github.com/storacha/go-ucanto/core/ipld" 8 "github.com/storacha/go-ucanto/core/receipt/fx" 9 "github.com/storacha/go-ucanto/core/result" 10 "github.com/storacha/go-ucanto/core/result/failure" 11 "github.com/storacha/go-ucanto/core/schema" 12 "github.com/storacha/go-ucanto/server/transaction" 13 "github.com/storacha/go-ucanto/ucan" 14 "github.com/storacha/go-ucanto/validator" 15 ) 16 17 type HandlerFunc[C any, O ipld.Builder, X failure.IPLDBuilderFailure] func( 18 ctx context.Context, 19 capability ucan.Capability[C], 20 invocation invocation.Invocation, 21 context InvocationContext, 22 ) (result result.Result[O, X], fx fx.Effects, err error) 23 24 // Provide is used to define given capability provider. It decorates the passed 25 // handler and takes care of UCAN validation. It only calls the handler 26 // when validation succeeds. 27 func Provide[C any, O ipld.Builder, X failure.IPLDBuilderFailure]( 28 capability validator.CapabilityParser[C], 29 handler HandlerFunc[C, O, X], 30 ) ServiceMethod[O, failure.IPLDBuilderFailure] { 31 return func(ctx context.Context, invocation invocation.Invocation, ictx InvocationContext) (transaction.Transaction[O, failure.IPLDBuilderFailure], error) { 32 vctx := validator.NewValidationContext( 33 ictx.ID().Verifier(), 34 capability, 35 ictx.CanIssue, 36 ictx.ValidateAuthorization, 37 ictx.ResolveProof, 38 ictx.ParsePrincipal, 39 ictx.ResolveDIDKey, 40 ictx.ValidateTimeBounds, 41 ictx.AuthorityProofs()..., 42 ) 43 44 // confirm the audience of the invocation is this service or any of the configured alternative audiences 45 acceptedAudiences := schema.Literal(ictx.ID().DID().String()) 46 if len(ictx.AlternativeAudiences()) > 0 { 47 altAudiences := make([]schema.Reader[string, string], 0, len(ictx.AlternativeAudiences())) 48 for _, a := range ictx.AlternativeAudiences() { 49 altAudiences = append(altAudiences, schema.Literal(a.DID().String())) 50 } 51 52 acceptedAudiences = schema.Or(append(altAudiences, acceptedAudiences)...) 53 } 54 55 if _, err := acceptedAudiences.Read(invocation.Audience().DID().String()); err != nil { 56 expectedAudiences := append([]ucan.Principal{ictx.ID()}, ictx.AlternativeAudiences()...) 57 audErr := NewInvalidAudienceError(invocation.Audience(), expectedAudiences...) 58 return transaction.NewTransaction(result.Error[O, failure.IPLDBuilderFailure](audErr)), nil 59 } 60 61 auth, aerr := validator.Access(ctx, invocation, vctx) 62 if aerr != nil { 63 return transaction.NewTransaction(result.Error[O](failure.FromError(aerr))), nil 64 } 65 66 res, fx, herr := handler(ctx, auth.Capability(), invocation, ictx) 67 if herr != nil { 68 return nil, herr 69 } 70 71 return transaction.NewTransaction( 72 result.MapResultR0( 73 res, 74 func(o O) O { return o }, 75 func(x X) failure.IPLDBuilderFailure { return x }, 76 ), 77 transaction.WithEffects(fx), 78 ), nil 79 } 80 }