go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/buildbucket/appengine/internal/buildtoken/buildtoken_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 buildtoken provide related functions for generating and parsing build
    16  // tokens.
    17  package buildtoken
    18  
    19  import (
    20  	"context"
    21  	"encoding/base64"
    22  	"testing"
    23  
    24  	"google.golang.org/protobuf/proto"
    25  
    26  	pb "go.chromium.org/luci/buildbucket/proto"
    27  	"go.chromium.org/luci/server/secrets"
    28  	"go.chromium.org/luci/server/secrets/testsecrets"
    29  
    30  	. "github.com/smartystreets/goconvey/convey"
    31  
    32  	. "go.chromium.org/luci/common/testing/assertions"
    33  )
    34  
    35  func mkToken(ctx context.Context, buildID int64, purpose pb.TokenBody_Purpose) string {
    36  	token, err := GenerateToken(ctx, buildID, purpose)
    37  	So(err, ShouldBeNil)
    38  	return token
    39  }
    40  
    41  func TestBuildToken(t *testing.T) {
    42  	t.Parallel()
    43  
    44  	Convey("build token", t, func() {
    45  		secretStore := &testsecrets.Store{
    46  			Secrets: map[string]secrets.Secret{
    47  				"somekey": {Active: []byte("i r key")},
    48  			},
    49  		}
    50  		ctx := secrets.Use(context.Background(), secretStore)
    51  		ctx = secrets.GeneratePrimaryTinkAEADForTest(ctx)
    52  
    53  		Convey("success (encrypted)", func() {
    54  			bID := int64(123)
    55  			token := mkToken(ctx, bID, pb.TokenBody_BUILD)
    56  			tBody, err := parseToTokenBodyImpl(ctx, token, 123, pb.TokenBody_BUILD)
    57  			So(err, ShouldBeNil)
    58  			So(tBody.BuildId, ShouldEqual, bID)
    59  			So(tBody.Purpose, ShouldEqual, pb.TokenBody_BUILD)
    60  			So(len(tBody.State), ShouldNotEqual, 0)
    61  		})
    62  
    63  		Convey("wrong build", func() {
    64  			bID := int64(123)
    65  			token := mkToken(ctx, bID, pb.TokenBody_BUILD)
    66  			_, err := parseToTokenBodyImpl(ctx, token, 321, pb.TokenBody_BUILD)
    67  			So(err, ShouldErrLike, "token is for build 123")
    68  		})
    69  
    70  		Convey("skip build check", func() {
    71  			bID := int64(123)
    72  			token := mkToken(ctx, bID, pb.TokenBody_BUILD)
    73  			tBody, err := parseToTokenBodyImpl(ctx, token, 0, pb.TokenBody_BUILD)
    74  			So(err, ShouldBeNil)
    75  			So(tBody, ShouldNotBeNil)
    76  		})
    77  
    78  		Convey("skip purpose check", func() {
    79  			bID := int64(123)
    80  			tBody, err := parseToTokenBodyImpl(ctx, mkToken(ctx, bID, pb.TokenBody_BUILD), bID)
    81  			So(err, ShouldBeNil)
    82  			So(tBody, ShouldNotBeNil)
    83  
    84  			tBody, err = parseToTokenBodyImpl(ctx, mkToken(ctx, bID, pb.TokenBody_TASK), bID)
    85  			So(err, ShouldBeNil)
    86  			So(tBody, ShouldNotBeNil)
    87  		})
    88  
    89  		Convey("multi purpose check", func() {
    90  			bID := int64(123)
    91  			token := mkToken(ctx, bID, pb.TokenBody_BUILD)
    92  			tBody, err := parseToTokenBodyImpl(ctx, token, bID, pb.TokenBody_TASK, pb.TokenBody_BUILD)
    93  			So(err, ShouldBeNil)
    94  			So(tBody, ShouldNotBeNil)
    95  
    96  			tBody, err = parseToTokenBodyImpl(ctx, token, bID, pb.TokenBody_BUILD, pb.TokenBody_TASK)
    97  			So(err, ShouldBeNil)
    98  			So(tBody, ShouldNotBeNil)
    99  		})
   100  
   101  		Convey("wrong purpose", func() {
   102  			bID := int64(123)
   103  			token := mkToken(ctx, bID, pb.TokenBody_BUILD)
   104  			_, err := parseToTokenBodyImpl(ctx, token, 123, pb.TokenBody_TASK)
   105  			So(err, ShouldErrLike, "token is for purpose BUILD")
   106  
   107  			_, err = parseToTokenBodyImpl(ctx, token, 123, pb.TokenBody_START_BUILD, pb.TokenBody_TASK)
   108  			So(err, ShouldErrLike, "token is for purpose BUILD")
   109  		})
   110  
   111  		Convey("not base64 encoded token", func() {
   112  			_, err := parseToTokenBodyImpl(ctx, "invalid token", 123, pb.TokenBody_BUILD)
   113  			So(err, ShouldErrLike, "error decoding token")
   114  		})
   115  
   116  		Convey("bad base64 encoded token", func() {
   117  			_, err := parseToTokenBodyImpl(ctx, "abckish", 123, pb.TokenBody_BUILD)
   118  			So(err, ShouldErrLike, "error unmarshalling token")
   119  		})
   120  
   121  		Convey("unsupported token version", func() {
   122  			tkBody := &pb.TokenBody{
   123  				BuildId: 1,
   124  				State:   []byte("random"),
   125  			}
   126  			tkBytes, err := proto.Marshal(tkBody)
   127  			So(err, ShouldBeNil)
   128  			tkEnvelop := &pb.TokenEnvelope{
   129  				Version: pb.TokenEnvelope_VERSION_UNSPECIFIED,
   130  				Payload: tkBytes,
   131  			}
   132  			tkeBytes, err := proto.Marshal(tkEnvelop)
   133  			So(err, ShouldBeNil)
   134  			_, err = parseToTokenBodyImpl(ctx, base64.RawURLEncoding.EncodeToString(tkeBytes), 123, pb.TokenBody_BUILD)
   135  			So(err, ShouldErrLike, "token with version 0 is not supported")
   136  		})
   137  	})
   138  }