1package customizations
2
3import (
4	"context"
5	"fmt"
6	"strings"
7
8	"github.com/aws/smithy-go/middleware"
9	smithyhttp "github.com/aws/smithy-go/transport/http"
10
11	"github.com/aws/aws-sdk-go-v2/aws"
12	awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
13	"github.com/aws/aws-sdk-go-v2/service/internal/s3shared"
14)
15
16// processOutpostIDMiddleware is special customization middleware to be applied for operations
17// CreateBucket, ListRegionalBuckets which must resolve endpoint to s3-outposts.{region}.amazonaws.com
18// with region as client region and signed by s3-control if an outpost id is provided.
19type processOutpostIDMiddleware struct {
20	// GetOutpostID points to a function that processes an input and returns an outpostID as string ptr,
21	// and bool indicating if outpostID is supported or set.
22	GetOutpostID func(interface{}) (*string, bool)
23
24	// UseDualStack indicates of dual stack endpoints should be used
25	UseDualstack bool
26}
27
28// ID returns the middleware ID.
29func (*processOutpostIDMiddleware) ID() string { return "S3Control:ProcessOutpostIDMiddleware" }
30
31// HandleSerialize adds a serialize step, this has to be before operation serializer and arn endpoint logic.
32// Ideally this step will be ahead of ARN customization for CreateBucket, ListRegionalBucket operation.
33func (m *processOutpostIDMiddleware) HandleSerialize(
34	ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler,
35) (
36	out middleware.SerializeOutput, metadata middleware.Metadata, err error,
37) {
38	// if host name is immutable, skip this customization
39	if smithyhttp.GetHostnameImmutable(ctx) {
40		return next.HandleSerialize(ctx, in)
41	}
42
43	// attempt to fetch an outpost id
44	outpostID, ok := m.GetOutpostID(in.Parameters)
45	if !ok {
46		return next.HandleSerialize(ctx, in)
47	}
48
49	// check if outpostID was not set or is empty
50	if outpostID == nil || len(strings.TrimSpace(*outpostID)) == 0 {
51		return next.HandleSerialize(ctx, in)
52	}
53
54	req, ok := in.Request.(*smithyhttp.Request)
55	if !ok {
56		return out, metadata, fmt.Errorf("unknown request type %T", req)
57	}
58
59	requestRegion := awsmiddleware.GetRegion(ctx)
60
61	// validate if fips
62	if s3shared.IsFIPS(requestRegion) {
63		return out, metadata, fmt.Errorf("unsupported fips region provided for outposts request")
64	}
65	// validate if dualstack
66	if m.UseDualstack {
67		return out, metadata, fmt.Errorf("dualstack is not supported for outposts request")
68	}
69
70	// if endpoint source is a custom endpoint source, do not modify host prefix.
71	// This is a requirement for s3 vpc interface endpoints.
72	if v := awsmiddleware.GetEndpointSource(ctx); v != aws.EndpointSourceCustom {
73		serviceEndpointLabel := "s3-outposts."
74
75		// set request url
76		req.URL.Host = serviceEndpointLabel + requestRegion + ".amazonaws.com"
77	}
78
79	// Disable endpoint host prefix for s3-control
80	ctx = smithyhttp.DisableEndpointHostPrefix(ctx, true)
81
82	// redirect signer
83	ctx = awsmiddleware.SetSigningName(ctx, "s3-outposts")
84	ctx = awsmiddleware.SetSigningRegion(ctx, requestRegion)
85
86	return next.HandleSerialize(ctx, in)
87}
88