go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/tokenserver/appengine/impl/delegation/rpc_import_delegation_configs_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 delegation
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	"go.chromium.org/luci/appengine/gaetesting"
    23  	"go.chromium.org/luci/common/clock/testclock"
    24  	"go.chromium.org/luci/config"
    25  	"go.chromium.org/luci/config/cfgclient"
    26  	"go.chromium.org/luci/config/impl/memory"
    27  	admin "go.chromium.org/luci/tokenserver/api/admin/v1"
    28  	"go.chromium.org/luci/tokenserver/appengine/impl/utils/policy"
    29  
    30  	. "github.com/smartystreets/goconvey/convey"
    31  	. "go.chromium.org/luci/common/testing/assertions"
    32  )
    33  
    34  func TestImportDelegationConfigs(t *testing.T) {
    35  	t.Parallel()
    36  
    37  	Convey("Works", t, func() {
    38  		ctx := gaetesting.TestingContext()
    39  		ctx, clk := testclock.UseTime(ctx, testclock.TestTimeUTC)
    40  
    41  		ctx = prepareCfg(ctx, `rules {
    42  			name: "rule 1"
    43  			requestor: "user:some-user@example.com"
    44  			target_service: "service:some-service"
    45  			allowed_to_impersonate: "group:some-group"
    46  			allowed_audience: "REQUESTOR"
    47  			max_validity_duration: 86400
    48  		}`)
    49  
    50  		rules := NewRulesCache()
    51  		rpc := ImportDelegationConfigsRPC{RulesCache: rules}
    52  
    53  		// No config.
    54  		r, err := rules.Rules(ctx)
    55  		So(err, ShouldEqual, policy.ErrNoPolicy)
    56  
    57  		resp, err := rpc.ImportDelegationConfigs(ctx, nil)
    58  		So(err, ShouldBeNil)
    59  		So(resp, ShouldResemble, &admin.ImportedConfigs{
    60  			Revision: "669865709d488bd6fbcec4fd87d6d1070253f42f",
    61  		})
    62  
    63  		// Have config now.
    64  		r, err = rules.Rules(ctx)
    65  		So(err, ShouldBeNil)
    66  		So(r.rules[0].rule.Name, ShouldEqual, "rule 1")
    67  		So(r.revision, ShouldEqual, "669865709d488bd6fbcec4fd87d6d1070253f42f")
    68  
    69  		// Noop import.
    70  		resp, err = rpc.ImportDelegationConfigs(ctx, nil)
    71  		So(err, ShouldBeNil)
    72  		So(resp.Revision, ShouldEqual, "669865709d488bd6fbcec4fd87d6d1070253f42f")
    73  
    74  		// Try to import completely broken config.
    75  		ctx = prepareCfg(ctx, `I'm broken`)
    76  		_, err = rpc.ImportDelegationConfigs(ctx, nil)
    77  		So(err, ShouldErrLike, `line 1.0: unknown field name`)
    78  
    79  		// Old config is not replaced.
    80  		r, _ = rules.Rules(ctx)
    81  		So(r.revision, ShouldEqual, "669865709d488bd6fbcec4fd87d6d1070253f42f")
    82  
    83  		// Try to import a config that doesn't pass validation.
    84  		ctx = prepareCfg(ctx, `rules {
    85  			name: "rule 1"
    86  		}`)
    87  		_, err = rpc.ImportDelegationConfigs(ctx, nil)
    88  		So(err, ShouldErrLike, `"requestor" is required (and 4 other errors)`)
    89  
    90  		// Old config is not replaced.
    91  		r, _ = rules.Rules(ctx)
    92  		So(r.revision, ShouldEqual, "669865709d488bd6fbcec4fd87d6d1070253f42f")
    93  
    94  		// Roll time to expire local rules cache.
    95  		clk.Add(10 * time.Minute)
    96  
    97  		// Have new config now!
    98  		ctx = prepareCfg(ctx, `rules {
    99  			name: "rule 2"
   100  			requestor: "user:some-user@example.com"
   101  			target_service: "service:some-service"
   102  			allowed_to_impersonate: "group:some-group"
   103  			allowed_audience: "REQUESTOR"
   104  			max_validity_duration: 86400
   105  		}`)
   106  
   107  		// Import it.
   108  		resp, err = rpc.ImportDelegationConfigs(ctx, nil)
   109  		So(err, ShouldBeNil)
   110  		So(resp, ShouldResemble, &admin.ImportedConfigs{
   111  			Revision: "7826cf5f5d3922ff363a2df0c363df088a81f261",
   112  		})
   113  
   114  		// It is now active.
   115  		r, err = rules.Rules(ctx)
   116  		So(err, ShouldBeNil)
   117  		So(r.rules[0].rule.Name, ShouldEqual, "rule 2")
   118  		So(r.revision, ShouldEqual, "7826cf5f5d3922ff363a2df0c363df088a81f261")
   119  	})
   120  }
   121  
   122  func prepareCfg(c context.Context, configFile string) context.Context {
   123  	return cfgclient.Use(c, memory.New(map[config.Set]memory.Files{
   124  		"services/${appid}": {
   125  			"delegation.cfg": configFile,
   126  		},
   127  	}))
   128  }