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  }