endpoints.go

  1// Code generated by smithy-go-codegen DO NOT EDIT.
  2
  3package ssooidc
  4
  5import (
  6	"context"
  7	"errors"
  8	"fmt"
  9	"github.com/aws/aws-sdk-go-v2/aws"
 10	awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
 11	internalConfig "github.com/aws/aws-sdk-go-v2/internal/configsources"
 12	"github.com/aws/aws-sdk-go-v2/internal/endpoints"
 13	"github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn"
 14	internalendpoints "github.com/aws/aws-sdk-go-v2/service/ssooidc/internal/endpoints"
 15	smithyauth "github.com/aws/smithy-go/auth"
 16	smithyendpoints "github.com/aws/smithy-go/endpoints"
 17	"github.com/aws/smithy-go/middleware"
 18	"github.com/aws/smithy-go/ptr"
 19	smithyhttp "github.com/aws/smithy-go/transport/http"
 20	"net/http"
 21	"net/url"
 22	"os"
 23	"strings"
 24)
 25
 26// EndpointResolverOptions is the service endpoint resolver options
 27type EndpointResolverOptions = internalendpoints.Options
 28
 29// EndpointResolver interface for resolving service endpoints.
 30type EndpointResolver interface {
 31	ResolveEndpoint(region string, options EndpointResolverOptions) (aws.Endpoint, error)
 32}
 33
 34var _ EndpointResolver = &internalendpoints.Resolver{}
 35
 36// NewDefaultEndpointResolver constructs a new service endpoint resolver
 37func NewDefaultEndpointResolver() *internalendpoints.Resolver {
 38	return internalendpoints.New()
 39}
 40
 41// EndpointResolverFunc is a helper utility that wraps a function so it satisfies
 42// the EndpointResolver interface. This is useful when you want to add additional
 43// endpoint resolving logic, or stub out specific endpoints with custom values.
 44type EndpointResolverFunc func(region string, options EndpointResolverOptions) (aws.Endpoint, error)
 45
 46func (fn EndpointResolverFunc) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) {
 47	return fn(region, options)
 48}
 49
 50// EndpointResolverFromURL returns an EndpointResolver configured using the
 51// provided endpoint url. By default, the resolved endpoint resolver uses the
 52// client region as signing region, and the endpoint source is set to
 53// EndpointSourceCustom.You can provide functional options to configure endpoint
 54// values for the resolved endpoint.
 55func EndpointResolverFromURL(url string, optFns ...func(*aws.Endpoint)) EndpointResolver {
 56	e := aws.Endpoint{URL: url, Source: aws.EndpointSourceCustom}
 57	for _, fn := range optFns {
 58		fn(&e)
 59	}
 60
 61	return EndpointResolverFunc(
 62		func(region string, options EndpointResolverOptions) (aws.Endpoint, error) {
 63			if len(e.SigningRegion) == 0 {
 64				e.SigningRegion = region
 65			}
 66			return e, nil
 67		},
 68	)
 69}
 70
 71type ResolveEndpoint struct {
 72	Resolver EndpointResolver
 73	Options  EndpointResolverOptions
 74}
 75
 76func (*ResolveEndpoint) ID() string {
 77	return "ResolveEndpoint"
 78}
 79
 80func (m *ResolveEndpoint) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) (
 81	out middleware.SerializeOutput, metadata middleware.Metadata, err error,
 82) {
 83	if !awsmiddleware.GetRequiresLegacyEndpoints(ctx) {
 84		return next.HandleSerialize(ctx, in)
 85	}
 86
 87	req, ok := in.Request.(*smithyhttp.Request)
 88	if !ok {
 89		return out, metadata, fmt.Errorf("unknown transport type %T", in.Request)
 90	}
 91
 92	if m.Resolver == nil {
 93		return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil")
 94	}
 95
 96	eo := m.Options
 97	eo.Logger = middleware.GetLogger(ctx)
 98
 99	var endpoint aws.Endpoint
100	endpoint, err = m.Resolver.ResolveEndpoint(awsmiddleware.GetRegion(ctx), eo)
101	if err != nil {
102		nf := (&aws.EndpointNotFoundError{})
103		if errors.As(err, &nf) {
104			ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, false)
105			return next.HandleSerialize(ctx, in)
106		}
107		return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err)
108	}
109
110	req.URL, err = url.Parse(endpoint.URL)
111	if err != nil {
112		return out, metadata, fmt.Errorf("failed to parse endpoint URL: %w", err)
113	}
114
115	if len(awsmiddleware.GetSigningName(ctx)) == 0 {
116		signingName := endpoint.SigningName
117		if len(signingName) == 0 {
118			signingName = "sso-oauth"
119		}
120		ctx = awsmiddleware.SetSigningName(ctx, signingName)
121	}
122	ctx = awsmiddleware.SetEndpointSource(ctx, endpoint.Source)
123	ctx = smithyhttp.SetHostnameImmutable(ctx, endpoint.HostnameImmutable)
124	ctx = awsmiddleware.SetSigningRegion(ctx, endpoint.SigningRegion)
125	ctx = awsmiddleware.SetPartitionID(ctx, endpoint.PartitionID)
126	return next.HandleSerialize(ctx, in)
127}
128func addResolveEndpointMiddleware(stack *middleware.Stack, o Options) error {
129	return stack.Serialize.Insert(&ResolveEndpoint{
130		Resolver: o.EndpointResolver,
131		Options:  o.EndpointOptions,
132	}, "OperationSerializer", middleware.Before)
133}
134
135func removeResolveEndpointMiddleware(stack *middleware.Stack) error {
136	_, err := stack.Serialize.Remove((&ResolveEndpoint{}).ID())
137	return err
138}
139
140type wrappedEndpointResolver struct {
141	awsResolver aws.EndpointResolverWithOptions
142}
143
144func (w *wrappedEndpointResolver) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) {
145	return w.awsResolver.ResolveEndpoint(ServiceID, region, options)
146}
147
148type awsEndpointResolverAdaptor func(service, region string) (aws.Endpoint, error)
149
150func (a awsEndpointResolverAdaptor) ResolveEndpoint(service, region string, options ...interface{}) (aws.Endpoint, error) {
151	return a(service, region)
152}
153
154var _ aws.EndpointResolverWithOptions = awsEndpointResolverAdaptor(nil)
155
156// withEndpointResolver returns an aws.EndpointResolverWithOptions that first delegates endpoint resolution to the awsResolver.
157// If awsResolver returns aws.EndpointNotFoundError error, the v1 resolver middleware will swallow the error,
158// and set an appropriate context flag such that fallback will occur when EndpointResolverV2 is invoked
159// via its middleware.
160//
161// If another error (besides aws.EndpointNotFoundError) is returned, then that error will be propagated.
162func withEndpointResolver(awsResolver aws.EndpointResolver, awsResolverWithOptions aws.EndpointResolverWithOptions) EndpointResolver {
163	var resolver aws.EndpointResolverWithOptions
164
165	if awsResolverWithOptions != nil {
166		resolver = awsResolverWithOptions
167	} else if awsResolver != nil {
168		resolver = awsEndpointResolverAdaptor(awsResolver.ResolveEndpoint)
169	}
170
171	return &wrappedEndpointResolver{
172		awsResolver: resolver,
173	}
174}
175
176func finalizeClientEndpointResolverOptions(options *Options) {
177	options.EndpointOptions.LogDeprecated = options.ClientLogMode.IsDeprecatedUsage()
178
179	if len(options.EndpointOptions.ResolvedRegion) == 0 {
180		const fipsInfix = "-fips-"
181		const fipsPrefix = "fips-"
182		const fipsSuffix = "-fips"
183
184		if strings.Contains(options.Region, fipsInfix) ||
185			strings.Contains(options.Region, fipsPrefix) ||
186			strings.Contains(options.Region, fipsSuffix) {
187			options.EndpointOptions.ResolvedRegion = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(
188				options.Region, fipsInfix, "-"), fipsPrefix, ""), fipsSuffix, "")
189			options.EndpointOptions.UseFIPSEndpoint = aws.FIPSEndpointStateEnabled
190		}
191	}
192
193}
194
195func resolveEndpointResolverV2(options *Options) {
196	if options.EndpointResolverV2 == nil {
197		options.EndpointResolverV2 = NewDefaultEndpointResolverV2()
198	}
199}
200
201func resolveBaseEndpoint(cfg aws.Config, o *Options) {
202	if cfg.BaseEndpoint != nil {
203		o.BaseEndpoint = cfg.BaseEndpoint
204	}
205
206	_, g := os.LookupEnv("AWS_ENDPOINT_URL")
207	_, s := os.LookupEnv("AWS_ENDPOINT_URL_SSO_OIDC")
208
209	if g && !s {
210		return
211	}
212
213	value, found, err := internalConfig.ResolveServiceBaseEndpoint(context.Background(), "SSO OIDC", cfg.ConfigSources)
214	if found && err == nil {
215		o.BaseEndpoint = &value
216	}
217}
218
219func bindRegion(region string) *string {
220	if region == "" {
221		return nil
222	}
223	return aws.String(endpoints.MapFIPSRegion(region))
224}
225
226// EndpointParameters provides the parameters that influence how endpoints are
227// resolved.
228type EndpointParameters struct {
229	// The AWS region used to dispatch the request.
230	//
231	// Parameter is
232	// required.
233	//
234	// AWS::Region
235	Region *string
236
237	// When true, use the dual-stack endpoint. If the configured endpoint does not
238	// support dual-stack, dispatching the request MAY return an error.
239	//
240	// Defaults to
241	// false if no value is provided.
242	//
243	// AWS::UseDualStack
244	UseDualStack *bool
245
246	// When true, send this request to the FIPS-compliant regional endpoint. If the
247	// configured endpoint does not have a FIPS compliant endpoint, dispatching the
248	// request will return an error.
249	//
250	// Defaults to false if no value is
251	// provided.
252	//
253	// AWS::UseFIPS
254	UseFIPS *bool
255
256	// Override the endpoint used to send this request
257	//
258	// Parameter is
259	// required.
260	//
261	// SDK::Endpoint
262	Endpoint *string
263}
264
265// ValidateRequired validates required parameters are set.
266func (p EndpointParameters) ValidateRequired() error {
267	if p.UseDualStack == nil {
268		return fmt.Errorf("parameter UseDualStack is required")
269	}
270
271	if p.UseFIPS == nil {
272		return fmt.Errorf("parameter UseFIPS is required")
273	}
274
275	return nil
276}
277
278// WithDefaults returns a shallow copy of EndpointParameterswith default values
279// applied to members where applicable.
280func (p EndpointParameters) WithDefaults() EndpointParameters {
281	if p.UseDualStack == nil {
282		p.UseDualStack = ptr.Bool(false)
283	}
284
285	if p.UseFIPS == nil {
286		p.UseFIPS = ptr.Bool(false)
287	}
288	return p
289}
290
291type stringSlice []string
292
293func (s stringSlice) Get(i int) *string {
294	if i < 0 || i >= len(s) {
295		return nil
296	}
297
298	v := s[i]
299	return &v
300}
301
302// EndpointResolverV2 provides the interface for resolving service endpoints.
303type EndpointResolverV2 interface {
304	// ResolveEndpoint attempts to resolve the endpoint with the provided options,
305	// returning the endpoint if found. Otherwise an error is returned.
306	ResolveEndpoint(ctx context.Context, params EndpointParameters) (
307		smithyendpoints.Endpoint, error,
308	)
309}
310
311// resolver provides the implementation for resolving endpoints.
312type resolver struct{}
313
314func NewDefaultEndpointResolverV2() EndpointResolverV2 {
315	return &resolver{}
316}
317
318// ResolveEndpoint attempts to resolve the endpoint with the provided options,
319// returning the endpoint if found. Otherwise an error is returned.
320func (r *resolver) ResolveEndpoint(
321	ctx context.Context, params EndpointParameters,
322) (
323	endpoint smithyendpoints.Endpoint, err error,
324) {
325	params = params.WithDefaults()
326	if err = params.ValidateRequired(); err != nil {
327		return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err)
328	}
329	_UseDualStack := *params.UseDualStack
330	_UseFIPS := *params.UseFIPS
331
332	if exprVal := params.Endpoint; exprVal != nil {
333		_Endpoint := *exprVal
334		_ = _Endpoint
335		if _UseFIPS == true {
336			return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported")
337		}
338		if _UseDualStack == true {
339			return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported")
340		}
341		uriString := _Endpoint
342
343		uri, err := url.Parse(uriString)
344		if err != nil {
345			return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
346		}
347
348		return smithyendpoints.Endpoint{
349			URI:     *uri,
350			Headers: http.Header{},
351		}, nil
352	}
353	if exprVal := params.Region; exprVal != nil {
354		_Region := *exprVal
355		_ = _Region
356		if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil {
357			_PartitionResult := *exprVal
358			_ = _PartitionResult
359			if _UseFIPS == true {
360				if _UseDualStack == true {
361					if true == _PartitionResult.SupportsFIPS {
362						if true == _PartitionResult.SupportsDualStack {
363							uriString := func() string {
364								var out strings.Builder
365								out.WriteString("https://oidc-fips.")
366								out.WriteString(_Region)
367								out.WriteString(".")
368								out.WriteString(_PartitionResult.DualStackDnsSuffix)
369								return out.String()
370							}()
371
372							uri, err := url.Parse(uriString)
373							if err != nil {
374								return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
375							}
376
377							return smithyendpoints.Endpoint{
378								URI:     *uri,
379								Headers: http.Header{},
380							}, nil
381						}
382					}
383					return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both")
384				}
385			}
386			if _UseFIPS == true {
387				if _PartitionResult.SupportsFIPS == true {
388					if _PartitionResult.Name == "aws-us-gov" {
389						uriString := func() string {
390							var out strings.Builder
391							out.WriteString("https://oidc.")
392							out.WriteString(_Region)
393							out.WriteString(".amazonaws.com")
394							return out.String()
395						}()
396
397						uri, err := url.Parse(uriString)
398						if err != nil {
399							return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
400						}
401
402						return smithyendpoints.Endpoint{
403							URI:     *uri,
404							Headers: http.Header{},
405						}, nil
406					}
407					uriString := func() string {
408						var out strings.Builder
409						out.WriteString("https://oidc-fips.")
410						out.WriteString(_Region)
411						out.WriteString(".")
412						out.WriteString(_PartitionResult.DnsSuffix)
413						return out.String()
414					}()
415
416					uri, err := url.Parse(uriString)
417					if err != nil {
418						return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
419					}
420
421					return smithyendpoints.Endpoint{
422						URI:     *uri,
423						Headers: http.Header{},
424					}, nil
425				}
426				return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS")
427			}
428			if _UseDualStack == true {
429				if true == _PartitionResult.SupportsDualStack {
430					uriString := func() string {
431						var out strings.Builder
432						out.WriteString("https://oidc.")
433						out.WriteString(_Region)
434						out.WriteString(".")
435						out.WriteString(_PartitionResult.DualStackDnsSuffix)
436						return out.String()
437					}()
438
439					uri, err := url.Parse(uriString)
440					if err != nil {
441						return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
442					}
443
444					return smithyendpoints.Endpoint{
445						URI:     *uri,
446						Headers: http.Header{},
447					}, nil
448				}
449				return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack")
450			}
451			uriString := func() string {
452				var out strings.Builder
453				out.WriteString("https://oidc.")
454				out.WriteString(_Region)
455				out.WriteString(".")
456				out.WriteString(_PartitionResult.DnsSuffix)
457				return out.String()
458			}()
459
460			uri, err := url.Parse(uriString)
461			if err != nil {
462				return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
463			}
464
465			return smithyendpoints.Endpoint{
466				URI:     *uri,
467				Headers: http.Header{},
468			}, nil
469		}
470		return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.")
471	}
472	return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region")
473}
474
475type endpointParamsBinder interface {
476	bindEndpointParams(*EndpointParameters)
477}
478
479func bindEndpointParams(ctx context.Context, input interface{}, options Options) *EndpointParameters {
480	params := &EndpointParameters{}
481
482	params.Region = bindRegion(options.Region)
483	params.UseDualStack = aws.Bool(options.EndpointOptions.UseDualStackEndpoint == aws.DualStackEndpointStateEnabled)
484	params.UseFIPS = aws.Bool(options.EndpointOptions.UseFIPSEndpoint == aws.FIPSEndpointStateEnabled)
485	params.Endpoint = options.BaseEndpoint
486
487	if b, ok := input.(endpointParamsBinder); ok {
488		b.bindEndpointParams(params)
489	}
490
491	return params
492}
493
494type resolveEndpointV2Middleware struct {
495	options Options
496}
497
498func (*resolveEndpointV2Middleware) ID() string {
499	return "ResolveEndpointV2"
500}
501
502func (m *resolveEndpointV2Middleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (
503	out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
504) {
505	if awsmiddleware.GetRequiresLegacyEndpoints(ctx) {
506		return next.HandleFinalize(ctx, in)
507	}
508
509	if err := checkAccountID(getIdentity(ctx), m.options.AccountIDEndpointMode); err != nil {
510		return out, metadata, fmt.Errorf("invalid accountID set: %w", err)
511	}
512
513	req, ok := in.Request.(*smithyhttp.Request)
514	if !ok {
515		return out, metadata, fmt.Errorf("unknown transport type %T", in.Request)
516	}
517
518	if m.options.EndpointResolverV2 == nil {
519		return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil")
520	}
521
522	params := bindEndpointParams(ctx, getOperationInput(ctx), m.options)
523	endpt, err := m.options.EndpointResolverV2.ResolveEndpoint(ctx, *params)
524	if err != nil {
525		return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err)
526	}
527
528	if endpt.URI.RawPath == "" && req.URL.RawPath != "" {
529		endpt.URI.RawPath = endpt.URI.Path
530	}
531	req.URL.Scheme = endpt.URI.Scheme
532	req.URL.Host = endpt.URI.Host
533	req.URL.Path = smithyhttp.JoinPath(endpt.URI.Path, req.URL.Path)
534	req.URL.RawPath = smithyhttp.JoinPath(endpt.URI.RawPath, req.URL.RawPath)
535	for k := range endpt.Headers {
536		req.Header.Set(k, endpt.Headers.Get(k))
537	}
538
539	rscheme := getResolvedAuthScheme(ctx)
540	if rscheme == nil {
541		return out, metadata, fmt.Errorf("no resolved auth scheme")
542	}
543
544	opts, _ := smithyauth.GetAuthOptions(&endpt.Properties)
545	for _, o := range opts {
546		rscheme.SignerProperties.SetAll(&o.SignerProperties)
547	}
548
549	return next.HandleFinalize(ctx, in)
550}