blob: ec7cb644fff8d88440ff3a6f8efc937f8b42e18a [file] [log] [blame]
// 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)
}
})
}
}