go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/auth/actor_access_tokens_test.go (about) 1 // Copyright 2017 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 "fmt" 20 "net/http" 21 "net/url" 22 "testing" 23 "time" 24 25 "go.chromium.org/luci/common/clock" 26 "go.chromium.org/luci/common/clock/testclock" 27 28 "go.chromium.org/luci/server/caching" 29 30 . "github.com/smartystreets/goconvey/convey" 31 ) 32 33 func TestMintAccessTokenForServiceAccount(t *testing.T) { 34 t.Parallel() 35 36 Convey("MintAccessTokenForServiceAccount works", t, func() { 37 ctx := context.Background() 38 ctx, _ = testclock.UseTime(ctx, testclock.TestRecentTimeUTC) 39 ctx = caching.WithEmptyProcessCache(ctx) 40 41 returnedToken := "token1" 42 lastRequest := "" 43 44 generateTokenURL := fmt.Sprintf("https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken?alt=json", 45 url.QueryEscape("abc@example.com")) 46 47 transport := &clientRPCTransportMock{ 48 cb: func(r *http.Request, body string) string { 49 lastRequest = body 50 51 expireTime, err := time.Parse(time.RFC3339, clock.Now(ctx).Add(time.Hour).UTC().Format(time.RFC3339)) 52 if err != nil { 53 t.Fatalf("Unable to parse/format time: %v", err) 54 } 55 56 if r.URL.String() == generateTokenURL { 57 return fmt.Sprintf(`{"accessToken":"%s","expireTime":"%s"}`, returnedToken, expireTime.Format(time.RFC3339)) 58 } 59 t.Fatalf("Unexpected request to %s", r.URL.String()) 60 return "unknown URL" 61 }, 62 } 63 64 ctx = ModifyConfig(ctx, func(cfg Config) Config { 65 cfg.AccessTokenProvider = transport.getAccessToken 66 cfg.AnonymousTransport = transport.getTransport 67 return cfg 68 }) 69 70 tok, err := MintAccessTokenForServiceAccount(ctx, MintAccessTokenParams{ 71 ServiceAccount: "abc@example.com", 72 Scopes: []string{"scope_b", "scope_a"}, 73 }) 74 So(err, ShouldBeNil) 75 76 expectedExpireTime, err := time.Parse(time.RFC3339, clock.Now(ctx).Add(time.Hour).UTC().Format(time.RFC3339)) 77 So(err, ShouldBeNil) 78 79 So(tok, ShouldResemble, &Token{ 80 Token: "token1", 81 Expiry: expectedExpireTime, 82 }) 83 84 // Cached now. 85 So(actorAccessTokenCache.lc.CachedLocally(ctx), ShouldEqual, 1) 86 87 // On subsequence request the cached token is used. 88 returnedToken = "token2" 89 tok, err = MintAccessTokenForServiceAccount(ctx, MintAccessTokenParams{ 90 ServiceAccount: "abc@example.com", 91 Scopes: []string{"scope_b", "scope_a"}, 92 }) 93 So(err, ShouldBeNil) 94 So(tok.Token, ShouldEqual, "token1") // old one 95 96 // Unless it expires sooner than requested TTL. 97 clock.Get(ctx).(testclock.TestClock).Add(40 * time.Minute) 98 tok, err = MintAccessTokenForServiceAccount(ctx, MintAccessTokenParams{ 99 ServiceAccount: "abc@example.com", 100 Scopes: []string{"scope_b", "scope_a"}, 101 MinTTL: 30 * time.Minute, 102 }) 103 So(err, ShouldBeNil) 104 So(tok.Token, ShouldResemble, "token2") // new one 105 106 // Using delegates results in a different cache key. 107 returnedToken = "token3" 108 tok, err = MintAccessTokenForServiceAccount(ctx, MintAccessTokenParams{ 109 ServiceAccount: "abc@example.com", 110 Scopes: []string{"scope_b", "scope_a"}, 111 Delegates: []string{"d2@example.com", "d1@example.com"}, 112 MinTTL: 30 * time.Minute, 113 }) 114 So(err, ShouldBeNil) 115 So(tok.Token, ShouldResemble, "token3") // new one 116 So(lastRequest, ShouldEqual, 117 `{"delegates":["projects/-/serviceAccounts/d2@example.com","projects/-/serviceAccounts/d1@example.com"],"scope":["scope_a","scope_b"]}`) 118 }) 119 }