go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/auth/actor_id_tokens_test.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package auth 16 17 import ( 18 "context" 19 "encoding/base64" 20 "fmt" 21 "net/http" 22 "net/url" 23 "testing" 24 "time" 25 26 "go.chromium.org/luci/common/clock" 27 "go.chromium.org/luci/common/clock/testclock" 28 29 "go.chromium.org/luci/server/caching" 30 31 . "github.com/smartystreets/goconvey/convey" 32 ) 33 34 func TestMintIDTokenForServiceAccount(t *testing.T) { 35 t.Parallel() 36 37 Convey("MintIDTokenForServiceAccount works", t, func() { 38 ctx := context.Background() 39 ctx, _ = testclock.UseTime(ctx, testclock.TestRecentTimeUTC) 40 ctx = caching.WithEmptyProcessCache(ctx) 41 42 // Will be changed throughout the test. 43 var returnedToken *Token 44 var lastRequest string 45 46 generateTokenURL := fmt.Sprintf( 47 "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateIdToken?alt=json", 48 url.QueryEscape("abc@example.com")) 49 50 transport := &clientRPCTransportMock{ 51 cb: func(r *http.Request, body string) string { 52 lastRequest = body 53 if r.URL.String() == generateTokenURL { 54 return fmt.Sprintf(`{"token":"%s"}`, returnedToken.Token) 55 } 56 t.Fatalf("Unexpected request to %s", r.URL.String()) 57 return "unknown URL" 58 }, 59 } 60 61 ctx = ModifyConfig(ctx, func(cfg Config) Config { 62 cfg.AccessTokenProvider = transport.getAccessToken 63 cfg.AnonymousTransport = transport.getTransport 64 return cfg 65 }) 66 67 token1 := genFakeIDToken(ctx, "token1", time.Hour) 68 token2 := genFakeIDToken(ctx, "token2", 2*time.Hour) 69 token3 := genFakeIDToken(ctx, "token3", 2*time.Hour) 70 71 returnedToken = token1 72 tok, err := MintIDTokenForServiceAccount(ctx, MintIDTokenParams{ 73 ServiceAccount: "abc@example.com", 74 Audience: "aud", 75 }) 76 So(err, ShouldBeNil) 77 So(tok, ShouldResemble, token1) 78 79 // Cached now. 80 So(actorIDTokenCache.lc.CachedLocally(ctx), ShouldEqual, 1) 81 82 // On subsequence request the cached token is used. 83 returnedToken = token2 84 tok, err = MintIDTokenForServiceAccount(ctx, MintIDTokenParams{ 85 ServiceAccount: "abc@example.com", 86 Audience: "aud", 87 }) 88 So(err, ShouldBeNil) 89 So(tok, ShouldResemble, token1) // old one 90 91 // Unless it expires sooner than requested TTL. 92 clock.Get(ctx).(testclock.TestClock).Add(40 * time.Minute) 93 tok, err = MintIDTokenForServiceAccount(ctx, MintIDTokenParams{ 94 ServiceAccount: "abc@example.com", 95 Audience: "aud", 96 MinTTL: 30 * time.Minute, 97 }) 98 So(err, ShouldBeNil) 99 So(tok, ShouldResemble, token2) 100 101 // Using delegates results in a different cache key. 102 returnedToken = token3 103 tok, err = MintIDTokenForServiceAccount(ctx, MintIDTokenParams{ 104 ServiceAccount: "abc@example.com", 105 Audience: "aud", 106 Delegates: []string{"d2@example.com", "d1@example.com"}, 107 MinTTL: 30 * time.Minute, 108 }) 109 So(err, ShouldBeNil) 110 So(tok, ShouldResemble, token3) // new one 111 So(lastRequest, ShouldEqual, 112 `{"delegates":["projects/-/serviceAccounts/d2@example.com","projects/-/serviceAccounts/d1@example.com"],"audience":"aud","includeEmail":true}`) 113 }) 114 } 115 116 func genFakeIDToken(ctx context.Context, name string, exp time.Duration) *Token { 117 expiry := clock.Now(ctx).Add(exp).Round(time.Second).UTC() 118 payload := fmt.Sprintf(`{"exp":%d}`, expiry.Unix()) 119 return &Token{ 120 Token: fmt.Sprintf("%s.%s.zzz", name, base64.RawURLEncoding.EncodeToString([]byte(payload))), 121 Expiry: expiry, 122 } 123 }