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