1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
// 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 downscope
import (
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"testing"
"time"
"google.golang.org/api/option"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/google/downscope"
storage "google.golang.org/api/storage/v1"
"google.golang.org/api/transport"
)
const (
rootTokenScope = "https://www.googleapis.com/auth/cloud-platform"
envServiceAccountFile = "GCLOUD_TESTS_GOLANG_KEY"
object1 = "cab-first-c45wknuy.txt"
object2 = "cab-second-c45wknuy.txt"
bucket = "dulcet-port-762"
)
var (
rootCredential *google.Credentials
)
// TestMain contains all of the setup code that needs to be run once before any of the tests are run
func TestMain(m *testing.M) {
flag.Parse()
if testing.Short() {
// This line runs all of our individual tests
os.Exit(m.Run())
}
ctx := context.Background()
credentialFileName := os.Getenv(envServiceAccountFile)
var err error
rootCredential, err = transport.Creds(ctx, option.WithCredentialsFile(credentialFileName), option.WithScopes(rootTokenScope))
if err != nil {
log.Fatalf("failed to construct root credential: %v", err)
}
// This line runs all of our individual tests
os.Exit(m.Run())
}
// downscopeTest holds the parameters necessary for running a test of the token downscoping capabilities implemented in `oauth2/google/downscope`
type downscopeTest struct {
name string
availableResource string
availablePermissions []string
condition downscope.AvailabilityCondition
objectName string
rootSource oauth2.TokenSource
expectError bool
}
func TestDownscopedToken(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
var downscopeTests = []downscopeTest{
{
name: "successfulDownscopedRead",
availableResource: "//storage.googleapis.com/projects/_/buckets/" + bucket,
availablePermissions: []string{"inRole:roles/storage.objectViewer"},
condition: downscope.AvailabilityCondition{
Expression: "resource.name.startsWith('projects/_/buckets/" + bucket + "/objects/" + object1 + "')",
},
rootSource: rootCredential.TokenSource,
objectName: object1,
expectError: false,
},
{
name: "readWithoutPermission",
availableResource: "//storage.googleapis.com/projects/_/buckets/" + bucket,
availablePermissions: []string{"inRole:roles/storage.objectViewer"},
condition: downscope.AvailabilityCondition{
Expression: "resource.name.startsWith('projects/_/buckets/" + bucket + "/objects/" + object1 + "')",
},
rootSource: rootCredential.TokenSource,
objectName: object2,
expectError: true,
},
}
for _, tt := range downscopeTests {
t.Run(tt.name, func(t *testing.T) {
err := downscopeQuery(t, tt)
// If a test isn't supposed to fail, it shouldn't fail.
if !tt.expectError && err != nil {
t.Errorf("test case %v should have succeeded, but instead returned %v", tt.name, err)
} else if tt.expectError && err == nil { // If a test is supposed to fail, it should return a non-nil error.
t.Errorf(" test case %v should have returned an error, but instead returned nil", tt.name)
}
})
}
}
// I'm not sure what I should name this according to convention.
func downscopeQuery(t *testing.T, tt downscopeTest) error {
t.Helper()
ctx := context.Background()
// Initializes an accessBoundary
var AccessBoundaryRules []downscope.AccessBoundaryRule
AccessBoundaryRules = append(AccessBoundaryRules, downscope.AccessBoundaryRule{AvailableResource: tt.availableResource, AvailablePermissions: tt.availablePermissions, Condition: &tt.condition})
downscopedTokenSource, err := downscope.NewTokenSource(context.Background(), downscope.DownscopingConfig{RootSource: tt.rootSource, Rules: AccessBoundaryRules})
if err != nil {
return fmt.Errorf("failed to create the initial token source: %v", err)
}
downscopedTokenSource = oauth2.ReuseTokenSource(nil, downscopedTokenSource)
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()
storageService, err := storage.NewService(ctx, option.WithTokenSource(downscopedTokenSource))
if err != nil {
return fmt.Errorf("failed to create the storage service: %v", err)
}
resp, err := storageService.Objects.Get(bucket, tt.objectName).Download()
if err != nil {
return fmt.Errorf("failed to retrieve object from GCP project with error: %v", err)
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("ioutil.ReadAll: %v", err)
}
return nil
}
|