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  }