// Copyright 2021 Google LLC. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package impersonate_test import ( "context" "flag" "fmt" "math/rand" "os" "testing" "time" admin "google.golang.org/api/admin/directory/v1" "google.golang.org/api/idtoken" "google.golang.org/api/impersonate" "google.golang.org/api/option" "google.golang.org/api/storage/v1" ) var ( baseKeyFile string readerKeyFile string readerEmail string writerEmail string projectID string domain string domainAdmin string ) func TestMain(m *testing.M) { flag.Parse() rand.Seed(time.Now().UnixNano()) baseKeyFile = os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") projectID = os.Getenv("GOOGLE_CLOUD_PROJECT") readerKeyFile = os.Getenv("GCLOUD_TESTS_IMPERSONATE_READER_KEY") readerEmail = os.Getenv("GCLOUD_TESTS_IMPERSONATE_READER_EMAIL") writerEmail = os.Getenv("GCLOUD_TESTS_IMPERSONATE_WRITER_EMAIL") domain = os.Getenv("GCLOUD_TESTS_IMPERSONATE_DOMAIN") domainAdmin = os.Getenv("GCLOUD_TESTS_IMPERSONATE_DOMAIN_ADMIN") os.Exit(m.Run()) } func validateEnvVars(t *testing.T) { t.Helper() if baseKeyFile == "" || readerKeyFile == "" || readerEmail == "" || writerEmail == "" || projectID == "" { t.Skip("required environment variable not set, skipping") } } func TestCredentialsTokenSourceIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } validateEnvVars(t) ctx := context.Background() tests := []struct { name string baseKeyFile string delegates []string }{ { name: "SA -> SA", baseKeyFile: readerKeyFile, }, { name: "SA -> Delegate -> SA", baseKeyFile: baseKeyFile, delegates: []string{readerEmail}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{ TargetPrincipal: writerEmail, Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"}, Delegates: tt.delegates, }, option.WithCredentialsFile(tt.baseKeyFile), ) if err != nil { t.Fatalf("failed to create ts: %v", err) } svc, err := storage.NewService(ctx, option.WithTokenSource(ts)) if err != nil { t.Fatalf("failed to create client: %v", err) } bucketName := fmt.Sprintf("%s-%d", projectID, rand.Int63()) if _, err := svc.Buckets.Insert(projectID, &storage.Bucket{ Name: bucketName, }).Do(); err != nil { t.Fatalf("error creating bucket: %v", err) } if err := svc.Buckets.Delete(bucketName).Do(); err != nil { t.Fatalf("unable to cleanup bucket %q: %v", bucketName, err) } }) } } func TestIDTokenSourceIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } validateEnvVars(t) ctx := context.Background() tests := []struct { name string baseKeyFile string delegates []string }{ { name: "SA -> SA", baseKeyFile: readerKeyFile, }, { name: "SA -> Delegate -> SA", baseKeyFile: baseKeyFile, delegates: []string{readerEmail}, }, } for _, tt := range tests { name := tt.name t.Run(name, func(t *testing.T) { aud := "http://example.com/" ts, err := impersonate.IDTokenSource(ctx, impersonate.IDTokenConfig{ TargetPrincipal: writerEmail, Audience: aud, Delegates: tt.delegates, IncludeEmail: true, }, option.WithCredentialsFile(tt.baseKeyFile), ) if err != nil { t.Fatalf("failed to create ts: %v", err) } tok, err := ts.Token() if err != nil { t.Fatalf("unable to retrieve Token: %v", err) } validTok, err := idtoken.Validate(context.Background(), tok.AccessToken, aud) if err != nil { t.Fatalf("token validation failed: %v", err) } if validTok.Audience != aud { t.Fatalf("got %q, want %q", validTok.Audience, aud) } if validTok.Claims["email"] != writerEmail { t.Fatalf("got %q, want %q", validTok.Claims["email"], writerEmail) } }) } } func TestTokenSourceIntegration_user(t *testing.T) { t.Skip("https://github.com/googleapis/google-api-go-client/issues/948") if testing.Short() { t.Skip("skipping integration test") } validateEnvVars(t) ctx := context.Background() tests := []struct { name string baseKeyFile string delegates []string }{ { name: "SA -> SA", baseKeyFile: readerKeyFile, }, { name: "SA -> Delegate -> SA", baseKeyFile: baseKeyFile, delegates: []string{readerEmail}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{ TargetPrincipal: writerEmail, Delegates: tt.delegates, Scopes: []string{"https://www.googleapis.com/auth/admin.directory.user", "https://www.googleapis.com/auth/admin.directory.group"}, Subject: domainAdmin, }, option.WithCredentialsFile(baseKeyFile), ) if err != nil { t.Fatalf("failed to create ts: %v", err) } svc, err := admin.NewService(ctx, option.WithTokenSource(ts)) if err != nil { t.Fatalf("failed to create client: %v", err) } if _, err := svc.Users.List().Domain(domain).Do(); err != nil { t.Fatalf("failed to list users: %v", err) } }) } }