go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/tokenserver/auth/machine/auth_method_test.go (about) 1 // Copyright 2016 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 machine 16 17 import ( 18 "context" 19 "encoding/base64" 20 "fmt" 21 "strings" 22 "testing" 23 "time" 24 25 "google.golang.org/protobuf/proto" 26 27 "go.chromium.org/luci/common/clock" 28 "go.chromium.org/luci/common/clock/testclock" 29 "go.chromium.org/luci/common/logging" 30 "go.chromium.org/luci/common/logging/memlogger" 31 "go.chromium.org/luci/server/auth" 32 "go.chromium.org/luci/server/auth/authtest" 33 "go.chromium.org/luci/server/auth/signing" 34 "go.chromium.org/luci/server/auth/signing/signingtest" 35 36 tokenserver "go.chromium.org/luci/tokenserver/api" 37 38 . "github.com/smartystreets/goconvey/convey" 39 ) 40 41 func TestMachineTokenAuthMethod(t *testing.T) { 42 Convey("with mock context", t, func() { 43 ctx := makeTestContext() 44 log := logging.Get(ctx).(*memlogger.MemLogger) 45 signer := signingtest.NewSigner(nil) 46 method := MachineTokenAuthMethod{ 47 certsFetcher: func(c context.Context, email string) (*signing.PublicCertificates, error) { 48 if email == "valid-signer@example.com" { 49 return signer.Certificates(c) 50 } 51 return nil, fmt.Errorf("unknown signer") 52 }, 53 } 54 55 mint := func(tok *tokenserver.MachineTokenBody, sig []byte) string { 56 body, _ := proto.Marshal(tok) 57 keyID, validSig, _ := signer.SignBytes(ctx, body) 58 if sig == nil { 59 sig = validSig 60 } 61 envelope, _ := proto.Marshal(&tokenserver.MachineTokenEnvelope{ 62 TokenBody: body, 63 KeyId: keyID, 64 RsaSha256: sig, 65 }) 66 return base64.RawStdEncoding.EncodeToString(envelope) 67 } 68 69 call := func(tok string) (*auth.User, error) { 70 r := authtest.NewFakeRequestMetadata() 71 if tok != "" { 72 r.FakeHeader.Set(MachineTokenHeader, tok) 73 } 74 u, _, err := method.Authenticate(ctx, r) 75 return u, err 76 } 77 78 hasLog := func(msg string) bool { 79 return log.HasFunc(func(m *memlogger.LogEntry) bool { 80 return strings.Contains(m.Msg, msg) 81 }) 82 } 83 84 Convey("valid token works", func() { 85 user, err := call(mint(&tokenserver.MachineTokenBody{ 86 MachineFqdn: "some-machine.location", 87 CaId: 123, 88 CertSn: []byte{1, 2, 3}, 89 IssuedBy: "valid-signer@example.com", 90 IssuedAt: uint64(clock.Now(ctx).Unix()), 91 Lifetime: 3600, 92 }, nil)) 93 So(err, ShouldBeNil) 94 So(user, ShouldResemble, &auth.User{ 95 Identity: "bot:some-machine.location", 96 Extra: &MachineTokenInfo{ 97 FQDN: "some-machine.location", 98 CA: 123, 99 CertSN: []byte{1, 2, 3}, 100 }, 101 }) 102 }) 103 104 Convey("not header => not applicable", func() { 105 user, err := call("") 106 So(user, ShouldBeNil) 107 So(err, ShouldBeNil) 108 }) 109 110 Convey("not base64 envelope", func() { 111 _, err := call("not-a-valid-token") 112 So(err, ShouldEqual, ErrBadToken) 113 So(hasLog("Failed to deserialize the token"), ShouldBeTrue) 114 }) 115 116 Convey("broken envelope", func() { 117 _, err := call("abcdef") 118 So(err, ShouldEqual, ErrBadToken) 119 So(hasLog("Failed to deserialize the token"), ShouldBeTrue) 120 }) 121 122 Convey("broken body", func() { 123 envelope, _ := proto.Marshal(&tokenserver.MachineTokenEnvelope{ 124 TokenBody: []byte("bad body"), 125 KeyId: "123", 126 RsaSha256: []byte("12345"), 127 }) 128 tok := base64.RawStdEncoding.EncodeToString(envelope) 129 _, err := call(tok) 130 So(err, ShouldEqual, ErrBadToken) 131 So(hasLog("Failed to deserialize the token"), ShouldBeTrue) 132 }) 133 134 Convey("bad signer ID", func() { 135 _, err := call(mint(&tokenserver.MachineTokenBody{ 136 MachineFqdn: "some-machine.location", 137 IssuedBy: "not-a-email.com", 138 IssuedAt: uint64(clock.Now(ctx).Unix()), 139 Lifetime: 3600, 140 }, nil)) 141 So(err, ShouldEqual, ErrBadToken) 142 So(hasLog("Bad issued_by field"), ShouldBeTrue) 143 }) 144 145 Convey("unknown signer", func() { 146 _, err := call(mint(&tokenserver.MachineTokenBody{ 147 MachineFqdn: "some-machine.location", 148 IssuedBy: "unknown-signer@example.com", 149 IssuedAt: uint64(clock.Now(ctx).Unix()), 150 Lifetime: 3600, 151 }, nil)) 152 So(err, ShouldEqual, ErrBadToken) 153 So(hasLog("Unknown token issuer"), ShouldBeTrue) 154 }) 155 156 Convey("not yet valid", func() { 157 _, err := call(mint(&tokenserver.MachineTokenBody{ 158 MachineFqdn: "some-machine.location", 159 IssuedBy: "valid-signer@example.com", 160 IssuedAt: uint64(clock.Now(ctx).Unix()) + 60, 161 Lifetime: 3600, 162 }, nil)) 163 So(err, ShouldEqual, ErrBadToken) 164 So(hasLog("Token has expired or not yet valid"), ShouldBeTrue) 165 }) 166 167 Convey("expired", func() { 168 _, err := call(mint(&tokenserver.MachineTokenBody{ 169 MachineFqdn: "some-machine.location", 170 IssuedBy: "valid-signer@example.com", 171 IssuedAt: uint64(clock.Now(ctx).Unix()) - 3620, 172 Lifetime: 3600, 173 }, nil)) 174 So(err, ShouldEqual, ErrBadToken) 175 So(hasLog("Token has expired or not yet valid"), ShouldBeTrue) 176 }) 177 178 Convey("bad signature", func() { 179 _, err := call(mint(&tokenserver.MachineTokenBody{ 180 MachineFqdn: "some-machine.location", 181 IssuedBy: "valid-signer@example.com", 182 IssuedAt: uint64(clock.Now(ctx).Unix()), 183 Lifetime: 3600, 184 }, []byte("bad signature"))) 185 So(err, ShouldEqual, ErrBadToken) 186 So(hasLog("Bad signature"), ShouldBeTrue) 187 }) 188 189 Convey("bad machine_fqdn", func() { 190 _, err := call(mint(&tokenserver.MachineTokenBody{ 191 MachineFqdn: "not::valid::machine::id", 192 IssuedBy: "valid-signer@example.com", 193 IssuedAt: uint64(clock.Now(ctx).Unix()), 194 Lifetime: 3600, 195 }, nil)) 196 So(err, ShouldEqual, ErrBadToken) 197 So(hasLog("Bad machine_fqdn"), ShouldBeTrue) 198 }) 199 }) 200 } 201 202 func makeTestContext() context.Context { 203 ctx := context.Background() 204 ctx, _ = testclock.UseTime(ctx, time.Date(2015, time.February, 3, 4, 5, 6, 7, time.UTC)) 205 ctx = memlogger.Use(ctx) 206 207 return authtest.NewFakeDB( 208 authtest.MockMembership("user:valid-signer@example.com", TokenServersGroup), 209 ).Use(ctx) 210 }