...

Source file src/cloud.google.com/go/auth/credentials/internal/externalaccount/impersonate_test.go

Documentation: cloud.google.com/go/auth/credentials/internal/externalaccount

     1  // Copyright 2023 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package externalaccount
    16  
    17  import (
    18  	"context"
    19  	"io"
    20  	"net/http"
    21  	"net/http/httptest"
    22  	"testing"
    23  
    24  	"cloud.google.com/go/auth/internal"
    25  )
    26  
    27  var (
    28  	baseImpersonateCredsReqBody  = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt"
    29  	baseImpersonateCredsRespBody = `{"accessToken":"Second.Access.Token","expireTime":"2020-12-28T15:01:23Z"}`
    30  )
    31  
    32  func TestImpersonation(t *testing.T) {
    33  	var impersonationTests = []struct {
    34  		name          string
    35  		opts          *Options
    36  		wantBody      string
    37  		metricsHeader string
    38  	}{
    39  		{
    40  			name: "Base Impersonation",
    41  			opts: &Options{
    42  				Audience:         "32555940559.apps.googleusercontent.com",
    43  				SubjectTokenType: jwtTokenType,
    44  				TokenInfoURL:     "http://localhost:8080/v1/tokeninfo",
    45  				ClientSecret:     "notsosecret",
    46  				ClientID:         "rbrgnognrhongo3bi4gb9ghg9g",
    47  				CredentialSource: testBaseCredSource,
    48  				Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
    49  			},
    50  			wantBody:      "{\"lifetime\":\"3600s\",\"scope\":[\"https://www.googleapis.com/auth/devstorage.full_control\"]}",
    51  			metricsHeader: expectedMetricsHeader("file", true, false),
    52  		},
    53  		{
    54  			name: "With TokenLifetime Set",
    55  			opts: &Options{
    56  				Audience:         "32555940559.apps.googleusercontent.com",
    57  				SubjectTokenType: jwtTokenType,
    58  				TokenInfoURL:     "http://localhost:8080/v1/tokeninfo",
    59  				ClientSecret:     "notsosecret",
    60  				ClientID:         "rbrgnognrhongo3bi4gb9ghg9g",
    61  				CredentialSource: testBaseCredSource,
    62  				Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
    63  				ServiceAccountImpersonationLifetimeSeconds: 10000,
    64  			},
    65  			wantBody:      "{\"lifetime\":\"10000s\",\"scope\":[\"https://www.googleapis.com/auth/devstorage.full_control\"]}",
    66  			metricsHeader: expectedMetricsHeader("file", true, false),
    67  		},
    68  	}
    69  	for _, tt := range impersonationTests {
    70  		t.Run(tt.name, func(t *testing.T) {
    71  			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    72  				if r.URL.Path == "/target" {
    73  					headerAuth := r.Header.Get("Authorization")
    74  					if got, want := headerAuth, "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want {
    75  						t.Errorf("got %v, want %v", got, want)
    76  					}
    77  					headerContentType := r.Header.Get("Content-Type")
    78  					if got, want := headerContentType, "application/x-www-form-urlencoded"; got != want {
    79  						t.Errorf("got %v, want %v", got, want)
    80  					}
    81  					body, err := io.ReadAll(r.Body)
    82  					if err != nil {
    83  						t.Fatalf("Failed reading request body: %v.", err)
    84  					}
    85  					if got, want := string(body), baseImpersonateCredsReqBody; got != want {
    86  						t.Errorf("got %v, want %v", got, want)
    87  					}
    88  					w.Header().Set("Content-Type", "application/json")
    89  					w.Write([]byte(baseCredsResponseBody))
    90  				} else if r.URL.Path == "/impersonate" {
    91  					headerAuth := r.Header.Get("Authorization")
    92  					if got, want := headerAuth, "Bearer Sample.Access.Token"; got != want {
    93  						t.Errorf("got %v, want %v", got, want)
    94  					}
    95  					headerContentType := r.Header.Get("Content-Type")
    96  					if got, want := headerContentType, "application/json"; got != want {
    97  						t.Errorf("got %v, want %v", got, want)
    98  					}
    99  					body, err := io.ReadAll(r.Body)
   100  					if err != nil {
   101  						t.Fatalf("Failed reading request body: %v.", err)
   102  					}
   103  					if got, want := string(body), tt.wantBody; got != want {
   104  						t.Errorf("got %v, want %v", got, want)
   105  					}
   106  					w.Header().Set("Content-Type", "application/json")
   107  					w.Write([]byte(baseImpersonateCredsRespBody))
   108  				} else {
   109  					t.Error("unmapped request")
   110  				}
   111  			}))
   112  			defer ts.Close()
   113  
   114  			testImpersonateOpts := tt.opts
   115  			testImpersonateOpts.ServiceAccountImpersonationURL = ts.URL + "/impersonate"
   116  			testImpersonateOpts.TokenURL = ts.URL + "/target"
   117  			testImpersonateOpts.Client = internal.CloneDefaultClient()
   118  
   119  			tp, err := NewTokenProvider(testImpersonateOpts)
   120  			if err != nil {
   121  				t.Fatalf("Failed to create Provider: %v", err)
   122  			}
   123  
   124  			oldNow := Now
   125  			defer func() { Now = oldNow }()
   126  			Now = testNow
   127  
   128  			tok, err := tp.Token(context.Background())
   129  			if err != nil {
   130  				t.Fatalf("tp.Token() = %v", err)
   131  			}
   132  			if got, want := tok.Value, "Second.Access.Token"; got != want {
   133  				t.Errorf("got %v, want %v", got, want)
   134  			}
   135  			if got, want := tok.Type, internal.TokenTypeBearer; got != want {
   136  				t.Errorf("got %v, want %v", got, want)
   137  			}
   138  		})
   139  	}
   140  }
   141  

View as plain text