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 }