go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/auth/openid/gcevm_test.go (about)

     1  // Copyright 2022 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 openid
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	"go.chromium.org/luci/common/clock"
    23  	"go.chromium.org/luci/common/clock/testclock"
    24  
    25  	"go.chromium.org/luci/server/auth"
    26  	"go.chromium.org/luci/server/auth/authtest"
    27  	"go.chromium.org/luci/server/auth/signing/signingtest"
    28  
    29  	. "github.com/smartystreets/goconvey/convey"
    30  	. "go.chromium.org/luci/common/testing/assertions"
    31  )
    32  
    33  func TestGoogleComputeAuthMethod(t *testing.T) {
    34  	t.Parallel()
    35  
    36  	ctx := context.Background()
    37  	ctx, _ = testclock.UseTime(ctx, time.Unix(1442540000, 0))
    38  
    39  	signer := signingtest.NewSigner(nil)
    40  	certs, _ := signer.Certificates(ctx)
    41  	keyID := signer.KeyNameForTest()
    42  
    43  	mintVMToken := func(tok IDToken) string {
    44  		return idTokenForTest(ctx, &tok, keyID, signer)
    45  	}
    46  
    47  	const fakeHost = "fake-host.example.com"
    48  
    49  	method := &GoogleComputeAuthMethod{
    50  		Header:        "X-Token-Header",
    51  		AudienceCheck: AudienceMatchesHost,
    52  		certs:         certs,
    53  	}
    54  	call := func(authHeader string) (*auth.User, error) {
    55  		req := authtest.NewFakeRequestMetadata()
    56  		req.FakeHost = fakeHost
    57  		req.FakeHeader.Set("X-Token-Header", authHeader)
    58  		u, _, err := method.Authenticate(ctx, req)
    59  		return u, err
    60  	}
    61  
    62  	Convey("Skipped if no header", t, func() {
    63  		user, err := call("")
    64  		So(err, ShouldBeNil)
    65  		So(user, ShouldBeNil)
    66  	})
    67  
    68  	Convey("Valid token", t, func() {
    69  		tok := IDToken{
    70  			Iss:           "https://accounts.google.com",
    71  			Sub:           "example@example.gserviceaccount.com",
    72  			Email:         "example@example.gserviceaccount.com",
    73  			EmailVerified: true,
    74  			Aud:           "https://" + fakeHost,
    75  			Iat:           clock.Now(ctx).Unix(),
    76  			Exp:           clock.Now(ctx).Add(time.Hour).Unix(),
    77  		}
    78  		tok.Google.ComputeEngine.ProjectID = "example.com:project-id"
    79  		tok.Google.ComputeEngine.Zone = "zone-id"
    80  		tok.Google.ComputeEngine.InstanceName = "instance-id"
    81  
    82  		user, err := call(mintVMToken(tok))
    83  		So(err, ShouldBeNil)
    84  		So(user, ShouldResemble, &auth.User{
    85  			Identity: "bot:instance-id@gce.project-id.example.com",
    86  			Extra: &GoogleComputeTokenInfo{
    87  				Audience:       "https://" + fakeHost,
    88  				ServiceAccount: "example@example.gserviceaccount.com",
    89  				Instance:       "instance-id",
    90  				Zone:           "zone-id",
    91  				Project:        "example.com:project-id",
    92  			},
    93  		})
    94  	})
    95  
    96  	Convey("No GCE info", t, func() {
    97  		tok := IDToken{
    98  			Iss:           "https://accounts.google.com",
    99  			Sub:           "example@example.gserviceaccount.com",
   100  			Email:         "example@example.gserviceaccount.com",
   101  			EmailVerified: true,
   102  			Aud:           "https://" + fakeHost,
   103  			Iat:           clock.Now(ctx).Unix(),
   104  			Exp:           clock.Now(ctx).Add(time.Hour).Unix(),
   105  		}
   106  
   107  		_, err := call(mintVMToken(tok))
   108  		So(err, ShouldErrLike, "no google.compute_engine in the GCE VM token")
   109  	})
   110  
   111  	Convey("Bad audience info", t, func() {
   112  		tok := IDToken{
   113  			Iss:           "https://accounts.google.com",
   114  			Sub:           "example@example.gserviceaccount.com",
   115  			Email:         "example@example.gserviceaccount.com",
   116  			EmailVerified: true,
   117  			Aud:           "https://WRONG",
   118  			Iat:           clock.Now(ctx).Unix(),
   119  			Exp:           clock.Now(ctx).Add(time.Hour).Unix(),
   120  		}
   121  		tok.Google.ComputeEngine.ProjectID = "example.com:project-id"
   122  		tok.Google.ComputeEngine.Zone = "zone-id"
   123  		tok.Google.ComputeEngine.InstanceName = "instance-id"
   124  
   125  		_, err := call(mintVMToken(tok))
   126  		So(err, ShouldEqual, auth.ErrBadAudience)
   127  	})
   128  }