golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/access/access_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package access 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "testing" 12 "time" 13 14 "github.com/google/go-cmp/cmp" 15 "google.golang.org/api/idtoken" 16 "google.golang.org/grpc/codes" 17 "google.golang.org/grpc/metadata" 18 "google.golang.org/grpc/status" 19 ) 20 21 func TestIAPFromContextError(t *testing.T) { 22 ctx := context.WithValue(context.Background(), contextIAP, "dance party") 23 if got, err := IAPFromContext(ctx); got != nil || err == nil { 24 t.Errorf("IAPFromContext(ctx) = %v, %s; want error", got, err) 25 } 26 } 27 28 func TestIAPAuthFunc(t *testing.T) { 29 want := &IAPFields{ 30 Email: "charlie@brown.com", 31 ID: "chaz.service.moo", 32 } 33 wantJWTToken := "eyJhb.eyJzdDIyfQ.Bh17Fl2gFjyLh6mo1GjqSPnGUg8MRLAE1Vdo3Z3gvdI" 34 wantAudience := "foo/bar/zar" 35 ctx := metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{ 36 iapHeaderJWT: wantJWTToken, 37 iapHeaderEmail: want.Email, 38 iapHeaderID: want.ID, 39 })) 40 testValidator := func(ctx context.Context, token, audience string) (*idtoken.Payload, error) { 41 if token != wantJWTToken || audience != wantAudience { 42 return nil, fmt.Errorf("testValidator(%q, %q); want %q, %q", token, audience, wantJWTToken, wantAudience) 43 } 44 return &idtoken.Payload{ 45 Issuer: "https://cloud.google.com/iap", 46 Audience: audience, 47 Expires: time.Now().Add(time.Minute).Unix(), 48 IssuedAt: time.Now().Add(-time.Minute).Unix(), 49 }, nil 50 } 51 authFunc := iapAuthFunc(wantAudience, testValidator) 52 gotCtx, err := authFunc(ctx) 53 if err != nil { 54 t.Fatalf("authFunc(ctx) = %+v, %s; want ctx, no error", gotCtx, err) 55 } 56 got, err := IAPFromContext(gotCtx) 57 if err != nil { 58 t.Fatalf("IAPFromContext(ctx) = %+v, %s; want no error", got, err) 59 } 60 if diff := cmp.Diff(got, want); diff != "" { 61 t.Errorf("ctx.Value(%v) mismatch (-got, +want):\n%s", contextIAP, diff) 62 } 63 } 64 65 func TestIAPAuthFuncError(t *testing.T) { 66 testCases := []struct { 67 desc string 68 validator validator 69 ctx context.Context 70 audience string 71 wantErr codes.Code 72 }{ 73 { 74 desc: "invalid context", 75 validator: func(ctx context.Context, token, audience string) (*idtoken.Payload, error) { 76 return &idtoken.Payload{ 77 Issuer: "https://cloud.google.com/iap", 78 Audience: audience, 79 Expires: time.Now().Add(time.Minute).Unix(), 80 IssuedAt: time.Now().Add(-time.Minute).Unix(), 81 }, nil 82 }, 83 ctx: context.Background(), 84 audience: "foo/bar/zar", 85 wantErr: codes.Internal, 86 }, 87 { 88 desc: "invalid jwt header", 89 validator: func(ctx context.Context, token, audience string) (*idtoken.Payload, error) { 90 return &idtoken.Payload{ 91 Issuer: "https://cloud.google.com/iap", 92 Audience: audience, 93 Expires: time.Now().Add(time.Minute).Unix(), 94 IssuedAt: time.Now().Add(-time.Minute).Unix(), 95 }, nil 96 }, 97 ctx: metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{ 98 iapHeaderEmail: "mary@foo.com", 99 iapHeaderID: "chaz.service.moo", 100 })), 101 audience: "foo/bar/zar", 102 wantErr: codes.Unauthenticated, 103 }, 104 { 105 desc: "failed validation", 106 validator: func(ctx context.Context, token, audience string) (*idtoken.Payload, error) { 107 return nil, errors.New("validation failed") 108 }, 109 ctx: metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{ 110 iapHeaderJWT: "xyz", 111 iapHeaderEmail: "mary@foo.com", 112 iapHeaderID: "chaz.service.moo", 113 })), 114 audience: "foo/bar/zar", 115 wantErr: codes.Unauthenticated, 116 }, 117 { 118 desc: "wrong issuer", 119 validator: func(ctx context.Context, token, audience string) (*idtoken.Payload, error) { 120 return &idtoken.Payload{ 121 Issuer: "https://cloud.google.com/iap-wrong", 122 Audience: audience, 123 Expires: time.Now().Add(time.Minute).Unix(), 124 IssuedAt: time.Now().Add(-time.Minute).Unix(), 125 }, nil 126 }, 127 ctx: metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{ 128 iapHeaderJWT: "xyz", 129 iapHeaderEmail: "mary@foo.com", 130 iapHeaderID: "chaz.service.moo", 131 })), 132 audience: "foo/bar/zar", 133 wantErr: codes.Unauthenticated, 134 }, 135 { 136 desc: "jwt expired", 137 validator: func(ctx context.Context, token, audience string) (*idtoken.Payload, error) { 138 return &idtoken.Payload{ 139 Issuer: "https://cloud.google.com/iap", 140 Audience: audience, 141 Expires: time.Now().Add(-time.Minute).Unix(), 142 IssuedAt: time.Now().Add(-10 * time.Minute).Unix(), 143 }, nil 144 }, 145 ctx: metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{ 146 iapHeaderJWT: "xyz", 147 iapHeaderEmail: "mary@foo.com", 148 iapHeaderID: "chaz.service.moo", 149 })), 150 audience: "foo/bar/zar", 151 wantErr: codes.Unauthenticated, 152 }, 153 } 154 for _, tc := range testCases { 155 t.Run(tc.desc, func(t *testing.T) { 156 authFunc := iapAuthFunc(tc.audience, tc.validator) 157 gotCtx, err := authFunc(tc.ctx) 158 if err == nil { 159 t.Fatalf("authFunc(ctx) = %s, nil; want error", gotCtx) 160 } 161 if status.Code(err) != tc.wantErr { 162 t.Fatalf("authFunc(ctx) = nil, %s; want %s", status.Code(err), tc.wantErr) 163 } 164 }) 165 } 166 } 167 168 func TestContextWithIAPMDError(t *testing.T) { 169 testCases := []struct { 170 desc string 171 md metadata.MD 172 }{ 173 { 174 desc: "missing email header", 175 md: metadata.New(map[string]string{ 176 iapHeaderJWT: "jwt", 177 iapHeaderID: "id", 178 }), 179 }, 180 { 181 desc: "missing id header", 182 md: metadata.New(map[string]string{ 183 iapHeaderJWT: "jwt", 184 iapHeaderEmail: "email", 185 }), 186 }, 187 } 188 for _, tc := range testCases { 189 t.Run(tc.desc, func(t *testing.T) { 190 ctx, err := contextWithIAPMD(context.Background(), tc.md) 191 if err == nil { 192 t.Errorf("contextWithIAPMD(ctx, %v) = %+v, %s; want ctx, error", tc.md, ctx, err) 193 } 194 }) 195 } 196 } 197 198 func TestIAPAudienceGCE(t *testing.T) { 199 want := "/projects/11/global/backendServices/bar" 200 if got := IAPAudienceGCE(11, "bar"); got != want { 201 t.Errorf("IAPAudienceGCE(11, bar) = %s; want %s", got, want) 202 } 203 } 204 205 func TestIAPAudience(t *testing.T) { 206 want := "/projects/11/apps/bar" 207 if got := IAPAudienceAppEngine(11, "bar"); got != want { 208 t.Errorf("IAPAudienceAppEngine(11, bar) = %s; want %s", got, want) 209 } 210 }