1
2
3 package sso
4
5 import (
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/sso/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
27 type EndpointResolverOptions = internalendpoints.Options
28
29
30 type EndpointResolver interface {
31 ResolveEndpoint(region string, options EndpointResolverOptions) (aws.Endpoint, error)
32 }
33
34 var _ EndpointResolver = &internalendpoints.Resolver{}
35
36
37 func NewDefaultEndpointResolver() *internalendpoints.Resolver {
38 return internalendpoints.New()
39 }
40
41
42
43
44 type EndpointResolverFunc func(region string, options EndpointResolverOptions) (aws.Endpoint, error)
45
46 func (fn EndpointResolverFunc) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) {
47 return fn(region, options)
48 }
49
50
51
52
53
54
55 func 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
71 type ResolveEndpoint struct {
72 Resolver EndpointResolver
73 Options EndpointResolverOptions
74 }
75
76 func (*ResolveEndpoint) ID() string {
77 return "ResolveEndpoint"
78 }
79
80 func (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 = "awsssoportal"
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 }
128 func 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
135 func removeResolveEndpointMiddleware(stack *middleware.Stack) error {
136 _, err := stack.Serialize.Remove((&ResolveEndpoint{}).ID())
137 return err
138 }
139
140 type wrappedEndpointResolver struct {
141 awsResolver aws.EndpointResolverWithOptions
142 }
143
144 func (w *wrappedEndpointResolver) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) {
145 return w.awsResolver.ResolveEndpoint(ServiceID, region, options)
146 }
147
148 type awsEndpointResolverAdaptor func(service, region string) (aws.Endpoint, error)
149
150 func (a awsEndpointResolverAdaptor) ResolveEndpoint(service, region string, options ...interface{}) (aws.Endpoint, error) {
151 return a(service, region)
152 }
153
154 var _ aws.EndpointResolverWithOptions = awsEndpointResolverAdaptor(nil)
155
156
157
158
159
160
161
162 func 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
176 func 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
195 func resolveEndpointResolverV2(options *Options) {
196 if options.EndpointResolverV2 == nil {
197 options.EndpointResolverV2 = NewDefaultEndpointResolverV2()
198 }
199 }
200
201 func 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")
208
209 if g && !s {
210 return
211 }
212
213 value, found, err := internalConfig.ResolveServiceBaseEndpoint(context.Background(), "SSO", cfg.ConfigSources)
214 if found && err == nil {
215 o.BaseEndpoint = &value
216 }
217 }
218
219 func bindRegion(region string) *string {
220 if region == "" {
221 return nil
222 }
223 return aws.String(endpoints.MapFIPSRegion(region))
224 }
225
226
227
228 type EndpointParameters struct {
229
230
231
232
233
234
235 Region *string
236
237
238
239
240
241
242
243
244 UseDualStack *bool
245
246
247
248
249
250
251
252
253
254 UseFIPS *bool
255
256
257
258
259
260
261
262 Endpoint *string
263 }
264
265
266 func (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
279
280 func (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
291
292 type EndpointResolverV2 interface {
293
294
295 ResolveEndpoint(ctx context.Context, params EndpointParameters) (
296 smithyendpoints.Endpoint, error,
297 )
298 }
299
300
301 type resolver struct{}
302
303 func NewDefaultEndpointResolverV2() EndpointResolverV2 {
304 return &resolver{}
305 }
306
307
308
309 func (r *resolver) ResolveEndpoint(
310 ctx context.Context, params EndpointParameters,
311 ) (
312 endpoint smithyendpoints.Endpoint, err error,
313 ) {
314 params = params.WithDefaults()
315 if err = params.ValidateRequired(); err != nil {
316 return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err)
317 }
318 _UseDualStack := *params.UseDualStack
319 _UseFIPS := *params.UseFIPS
320
321 if exprVal := params.Endpoint; exprVal != nil {
322 _Endpoint := *exprVal
323 _ = _Endpoint
324 if _UseFIPS == true {
325 return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported")
326 }
327 if _UseDualStack == true {
328 return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported")
329 }
330 uriString := _Endpoint
331
332 uri, err := url.Parse(uriString)
333 if err != nil {
334 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
335 }
336
337 return smithyendpoints.Endpoint{
338 URI: *uri,
339 Headers: http.Header{},
340 }, nil
341 }
342 if exprVal := params.Region; exprVal != nil {
343 _Region := *exprVal
344 _ = _Region
345 if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil {
346 _PartitionResult := *exprVal
347 _ = _PartitionResult
348 if _UseFIPS == true {
349 if _UseDualStack == true {
350 if true == _PartitionResult.SupportsFIPS {
351 if true == _PartitionResult.SupportsDualStack {
352 uriString := func() string {
353 var out strings.Builder
354 out.WriteString("https://portal.sso-fips.")
355 out.WriteString(_Region)
356 out.WriteString(".")
357 out.WriteString(_PartitionResult.DualStackDnsSuffix)
358 return out.String()
359 }()
360
361 uri, err := url.Parse(uriString)
362 if err != nil {
363 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
364 }
365
366 return smithyendpoints.Endpoint{
367 URI: *uri,
368 Headers: http.Header{},
369 }, nil
370 }
371 }
372 return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both")
373 }
374 }
375 if _UseFIPS == true {
376 if true == _PartitionResult.SupportsFIPS {
377 if "aws-us-gov" == _PartitionResult.Name {
378 uriString := func() string {
379 var out strings.Builder
380 out.WriteString("https://portal.sso.")
381 out.WriteString(_Region)
382 out.WriteString(".amazonaws.com")
383 return out.String()
384 }()
385
386 uri, err := url.Parse(uriString)
387 if err != nil {
388 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
389 }
390
391 return smithyendpoints.Endpoint{
392 URI: *uri,
393 Headers: http.Header{},
394 }, nil
395 }
396 uriString := func() string {
397 var out strings.Builder
398 out.WriteString("https://portal.sso-fips.")
399 out.WriteString(_Region)
400 out.WriteString(".")
401 out.WriteString(_PartitionResult.DnsSuffix)
402 return out.String()
403 }()
404
405 uri, err := url.Parse(uriString)
406 if err != nil {
407 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
408 }
409
410 return smithyendpoints.Endpoint{
411 URI: *uri,
412 Headers: http.Header{},
413 }, nil
414 }
415 return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS")
416 }
417 if _UseDualStack == true {
418 if true == _PartitionResult.SupportsDualStack {
419 uriString := func() string {
420 var out strings.Builder
421 out.WriteString("https://portal.sso.")
422 out.WriteString(_Region)
423 out.WriteString(".")
424 out.WriteString(_PartitionResult.DualStackDnsSuffix)
425 return out.String()
426 }()
427
428 uri, err := url.Parse(uriString)
429 if err != nil {
430 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
431 }
432
433 return smithyendpoints.Endpoint{
434 URI: *uri,
435 Headers: http.Header{},
436 }, nil
437 }
438 return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack")
439 }
440 uriString := func() string {
441 var out strings.Builder
442 out.WriteString("https://portal.sso.")
443 out.WriteString(_Region)
444 out.WriteString(".")
445 out.WriteString(_PartitionResult.DnsSuffix)
446 return out.String()
447 }()
448
449 uri, err := url.Parse(uriString)
450 if err != nil {
451 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
452 }
453
454 return smithyendpoints.Endpoint{
455 URI: *uri,
456 Headers: http.Header{},
457 }, nil
458 }
459 return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.")
460 }
461 return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region")
462 }
463
464 type endpointParamsBinder interface {
465 bindEndpointParams(*EndpointParameters)
466 }
467
468 func bindEndpointParams(input interface{}, options Options) *EndpointParameters {
469 params := &EndpointParameters{}
470
471 params.Region = bindRegion(options.Region)
472 params.UseDualStack = aws.Bool(options.EndpointOptions.UseDualStackEndpoint == aws.DualStackEndpointStateEnabled)
473 params.UseFIPS = aws.Bool(options.EndpointOptions.UseFIPSEndpoint == aws.FIPSEndpointStateEnabled)
474 params.Endpoint = options.BaseEndpoint
475
476 if b, ok := input.(endpointParamsBinder); ok {
477 b.bindEndpointParams(params)
478 }
479
480 return params
481 }
482
483 type resolveEndpointV2Middleware struct {
484 options Options
485 }
486
487 func (*resolveEndpointV2Middleware) ID() string {
488 return "ResolveEndpointV2"
489 }
490
491 func (m *resolveEndpointV2Middleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (
492 out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
493 ) {
494 if awsmiddleware.GetRequiresLegacyEndpoints(ctx) {
495 return next.HandleFinalize(ctx, in)
496 }
497
498 req, ok := in.Request.(*smithyhttp.Request)
499 if !ok {
500 return out, metadata, fmt.Errorf("unknown transport type %T", in.Request)
501 }
502
503 if m.options.EndpointResolverV2 == nil {
504 return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil")
505 }
506
507 params := bindEndpointParams(getOperationInput(ctx), m.options)
508 endpt, err := m.options.EndpointResolverV2.ResolveEndpoint(ctx, *params)
509 if err != nil {
510 return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err)
511 }
512
513 if endpt.URI.RawPath == "" && req.URL.RawPath != "" {
514 endpt.URI.RawPath = endpt.URI.Path
515 }
516 req.URL.Scheme = endpt.URI.Scheme
517 req.URL.Host = endpt.URI.Host
518 req.URL.Path = smithyhttp.JoinPath(endpt.URI.Path, req.URL.Path)
519 req.URL.RawPath = smithyhttp.JoinPath(endpt.URI.RawPath, req.URL.RawPath)
520 for k := range endpt.Headers {
521 req.Header.Set(k, endpt.Headers.Get(k))
522 }
523
524 rscheme := getResolvedAuthScheme(ctx)
525 if rscheme == nil {
526 return out, metadata, fmt.Errorf("no resolved auth scheme")
527 }
528
529 opts, _ := smithyauth.GetAuthOptions(&endpt.Properties)
530 for _, o := range opts {
531 rscheme.SignerProperties.SetAll(&o.SignerProperties)
532 }
533
534 return next.HandleFinalize(ctx, in)
535 }
536
View as plain text