github.com/mweagle/Sparta@v1.15.0/archetype/s3.go (about)

     1  package archetype
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"runtime"
     7  
     8  	awsLambdaEvents "github.com/aws/aws-lambda-go/events"
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/service/s3"
    11  	sparta "github.com/mweagle/Sparta"
    12  	spartaCF "github.com/mweagle/Sparta/aws/cloudformation"
    13  	gocf "github.com/mweagle/go-cloudformation"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // ReactorNameProvider is an interface so that a reactor function can
    18  // provide a custom name which prevents collisions
    19  type ReactorNameProvider interface {
    20  	ReactorName() string
    21  }
    22  
    23  // S3Reactor represents a lambda function that responds to typical S3 operations
    24  type S3Reactor interface {
    25  	// OnS3Event when an S3 event occurs. Check the event.EventName field
    26  	// for the specific event
    27  	OnS3Event(ctx context.Context, event awsLambdaEvents.S3Event) (interface{}, error)
    28  }
    29  
    30  // S3ReactorFunc is a free function that adapts a S3Reactor
    31  // compliant signature into a function that exposes an OnS3Event
    32  // function
    33  type S3ReactorFunc func(ctx context.Context, event awsLambdaEvents.S3Event) (interface{}, error)
    34  
    35  // OnS3Event satisfies the S3Reactor interface
    36  func (reactorFunc S3ReactorFunc) OnS3Event(ctx context.Context, event awsLambdaEvents.S3Event) (interface{}, error) {
    37  	return reactorFunc(ctx, event)
    38  }
    39  
    40  // ReactorName provides the name of the reactor func
    41  func (reactorFunc S3ReactorFunc) ReactorName() string {
    42  	return runtime.FuncForPC(reflect.ValueOf(reactorFunc).Pointer()).Name()
    43  }
    44  
    45  // s3NotificationPrefixFilter is a DRY spec for setting up a notification configuration
    46  // filter
    47  func s3NotificationPrefixBasedPermission(bucketName gocf.Stringable, keyPathPrefix string) sparta.S3Permission {
    48  
    49  	permission := sparta.S3Permission{
    50  		BasePermission: sparta.BasePermission{
    51  			SourceArn: bucketName.String(),
    52  		},
    53  		Events: []string{"s3:ObjectCreated:*",
    54  			"s3:ObjectRemoved:*"},
    55  	}
    56  
    57  	if keyPathPrefix != "" {
    58  		permission.Filter = s3.NotificationConfigurationFilter{
    59  			Key: &s3.KeyFilter{
    60  				FilterRules: []*s3.FilterRule{{
    61  					Name:  aws.String("prefix"),
    62  					Value: aws.String(keyPathPrefix),
    63  				}},
    64  			},
    65  		}
    66  	}
    67  	return permission
    68  }
    69  
    70  // NewS3Reactor returns an S3 reactor lambda function
    71  func NewS3Reactor(reactor S3Reactor, s3Bucket gocf.Stringable, additionalLambdaPermissions []sparta.IAMRolePrivilege) (*sparta.LambdaAWSInfo, error) {
    72  	return NewS3ScopedReactor(reactor, s3Bucket, "", additionalLambdaPermissions)
    73  }
    74  
    75  // NewS3ScopedReactor returns an S3 reactor lambda function scoped to the given S3 key prefix
    76  func NewS3ScopedReactor(reactor S3Reactor,
    77  	s3Bucket gocf.Stringable,
    78  	keyPathPrefix string,
    79  	additionalLambdaPermissions []sparta.IAMRolePrivilege) (*sparta.LambdaAWSInfo, error) {
    80  
    81  	reactorLambda := func(ctx context.Context, event awsLambdaEvents.S3Event) (interface{}, error) {
    82  		return reactor.OnS3Event(ctx, event)
    83  	}
    84  
    85  	// Privilege must include access to the S3 bucket for GetObjectRequest
    86  	lambdaFn, lambdaFnErr := sparta.NewAWSLambda(reactorName(reactor),
    87  		reactorLambda,
    88  		sparta.IAMRoleDefinition{})
    89  	if lambdaFnErr != nil {
    90  		return nil, errors.Wrapf(lambdaFnErr, "attempting to create reactor")
    91  	}
    92  
    93  	privileges := []sparta.IAMRolePrivilege{{
    94  		Actions:  []string{"s3:GetObject"},
    95  		Resource: spartaCF.S3AllKeysArnForBucket(s3Bucket),
    96  	}}
    97  	if len(additionalLambdaPermissions) != 0 {
    98  		privileges = append(privileges, additionalLambdaPermissions...)
    99  	}
   100  
   101  	// IAM Role privileges
   102  	lambdaFn.RoleDefinition.Privileges = privileges
   103  
   104  	// Event Triggers
   105  	lambdaFn.Permissions = append(lambdaFn.Permissions,
   106  		s3NotificationPrefixBasedPermission(s3Bucket, keyPathPrefix))
   107  
   108  	return lambdaFn, nil
   109  }