Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Unified Diff: tokenserver/appengine/delegation/config_test.go

Issue 2413683004: token-server: Delegation config import, validation and evaluation. (Closed)
Patch Set: also check validity_duration Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tokenserver/appengine/delegation/config_test.go
diff --git a/tokenserver/appengine/delegation/config_test.go b/tokenserver/appengine/delegation/config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b151359a92522a7fe71a19502d9379dfdbbf7c50
--- /dev/null
+++ b/tokenserver/appengine/delegation/config_test.go
@@ -0,0 +1,341 @@
+// Copyright 2016 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package delegation
+
+import (
+ "testing"
+ "time"
+
+ "github.com/golang/protobuf/proto"
+ "golang.org/x/net/context"
+
+ "github.com/luci/gae/service/datastore"
+ "github.com/luci/luci-go/appengine/gaetesting"
+ "github.com/luci/luci-go/common/clock/testclock"
+ "github.com/luci/luci-go/server/auth"
+ "github.com/luci/luci-go/server/auth/authtest"
+ "github.com/luci/luci-go/server/auth/identity"
+ admin "github.com/luci/luci-go/tokenserver/api/admin/v1"
+ "github.com/luci/luci-go/tokenserver/appengine/utils/identityset"
+
+ . "github.com/luci/luci-go/common/testing/assertions"
+ . "github.com/smartystreets/goconvey/convey"
+)
+
+func TestDelegationConfigLoader(t *testing.T) {
+ Convey("DelegationConfigLoader works", t, func() {
+ ctx := gaetesting.TestingContext()
+ ctx, tc := testclock.UseTime(ctx, testclock.TestTimeUTC)
+
+ loader := DelegationConfigLoader()
+
+ // Put the initial copy into the datastore.
+ cfg, err := loadConfig(`
+ rules {
+ name: "rule 1"
+ requestor: "user:some-user@example.com"
+ target_service: "service:some-service"
+ allowed_to_impersonate: "group:some-group"
+ allowed_audience: "REQUESTOR"
+ max_validity_duration: 86400
+ }`)
+ So(err, ShouldBeNil)
+ cfg.Revision = "1"
+ So(datastore.Put(ctx, cfg), ShouldBeNil)
+
+ // Loader fetches it.
+ fetched1, err := loader(ctx)
+ So(err, ShouldBeNil)
+ So(fetched1.ParsedConfig.Rules[0].Name, ShouldEqual, "rule 1")
+
+ // Config is updated.
+ cfg, err = loadConfig(`
+ rules {
+ name: "rule 2"
+ requestor: "user:some-user@example.com"
+ target_service: "service:some-service"
+ allowed_to_impersonate: "group:some-group"
+ allowed_audience: "REQUESTOR"
+ max_validity_duration: 86400
+ }`)
+ So(err, ShouldBeNil)
+ cfg.Revision = "2"
+ So(datastore.Put(ctx, cfg), ShouldBeNil)
+
+ // Loader still returns old cached copy.
+ fetched2, err := loader(ctx)
+ So(err, ShouldBeNil)
+ So(fetched2, ShouldEqual, fetched1)
+
+ // Advance time to expire the cache. The new copy is fetched.
+ tc.Add(procCacheExpiration + time.Second)
+ fetched3, err := loader(ctx)
+ So(err, ShouldBeNil)
+ So(fetched3.ParsedConfig.Rules[0].Name, ShouldEqual, "rule 2")
+
+ // Advance time again, but do not change the config. Loader reuses existing
+ // object.
+ tc.Add(procCacheExpiration + time.Second)
+ fetched4, err := loader(ctx)
+ So(err, ShouldBeNil)
+ So(fetched4, ShouldEqual, fetched3)
+ })
+}
+
+func TestIsAuthorizedRequestor(t *testing.T) {
+ Convey("IsAuthorizedRequestor works", t, func() {
+ cfg, err := loadConfig(`
+ rules {
+ name: "rule 1"
+ requestor: "user:some-user@example.com"
+
+ target_service: "service:some-service"
+ allowed_to_impersonate: "group:some-group"
+ allowed_audience: "REQUESTOR"
+ max_validity_duration: 86400
+ }
+
+ rules {
+ name: "rule 2"
+ requestor: "user:some-another-user@example.com"
+ requestor: "group:some-group"
+
+ target_service: "service:some-service"
+ allowed_to_impersonate: "group:some-group"
+ allowed_audience: "REQUESTOR"
+ max_validity_duration: 86400
+ }
+ `)
+ So(err, ShouldBeNil)
+ So(cfg, ShouldNotBeNil)
+
+ ctx := auth.WithState(context.Background(), &authtest.FakeState{
+ Identity: "user:some-user@example.com",
+ })
+ res, err := cfg.IsAuthorizedRequestor(ctx, identity.Identity("user:some-user@example.com"))
+ So(err, ShouldBeNil)
+ So(res, ShouldBeTrue)
+
+ ctx = auth.WithState(context.Background(), &authtest.FakeState{
+ Identity: "user:some-another-user@example.com",
+ })
+ res, err = cfg.IsAuthorizedRequestor(ctx, identity.Identity("user:some-another-user@example.com"))
+ So(err, ShouldBeNil)
+ So(res, ShouldBeTrue)
+
+ ctx = auth.WithState(context.Background(), &authtest.FakeState{
+ Identity: "user:unknown-user@example.com",
+ })
+ res, err = cfg.IsAuthorizedRequestor(ctx, identity.Identity("user:unknown-user@example.com"))
+ So(err, ShouldBeNil)
+ So(res, ShouldBeFalse)
+
+ ctx = auth.WithState(context.Background(), &authtest.FakeState{
+ Identity: "user:via-group@example.com",
+ IdentityGroups: []string{"some-group"},
+ })
+ res, err = cfg.IsAuthorizedRequestor(ctx, identity.Identity("user:via-group@example.com"))
+ So(err, ShouldBeNil)
+ So(res, ShouldBeTrue)
+ })
+}
+
+func TestFindMatchingRule(t *testing.T) {
+ Convey("with example config", t, func() {
+ cfg, err := loadConfig(`
+ rules {
+ name: "rule 1"
+ requestor: "user:requestor@example.com"
+ target_service: "service:some-service"
+ allowed_to_impersonate: "user:allowed-to-impersonate@example.com"
+ allowed_audience: "user:allowed-audience@example.com"
+ max_validity_duration: 86400
+ }
+
+ rules {
+ name: "rule 2"
+ requestor: "group:requestor-group"
+ target_service: "service:some-service"
+ allowed_to_impersonate: "group:delegatees-group"
+ allowed_audience: "group:audience-group"
+ max_validity_duration: 86400
+ }
+
+ rules {
+ name: "rule 3"
+ requestor: "group:requestor-group"
+ target_service: "service:some-service"
+ allowed_to_impersonate: "REQUESTOR"
+ allowed_audience: "REQUESTOR"
+ max_validity_duration: 86400
+ }
+
+ rules {
+ name: "rule 4"
+ requestor: "user:some-requestor@example.com"
+ requestor: "user:conflicts-with-rule-5@example.com"
+ target_service: "*"
+ allowed_to_impersonate: "REQUESTOR"
+ allowed_audience: "*"
+ max_validity_duration: 86400
+ }
+
+ rules {
+ name: "rule 5"
+ requestor: "user:conflicts-with-rule-5@example.com"
+ target_service: "*"
+ allowed_to_impersonate: "REQUESTOR"
+ allowed_audience: "*"
+ max_validity_duration: 86400
+ }
+ `)
+ So(err, ShouldBeNil)
+ So(cfg, ShouldNotBeNil)
+
+ ctx := auth.WithState(context.Background(), &authtest.FakeState{
+ Identity: "user:requestor@example.com",
+ FakeDB: authtest.FakeDB{
+ "user:requestor-group-member@example.com": []string{"requestor-group"},
+ "user:delegatees-group-member@example.com": []string{"delegatees-group"},
+ "user:audience-group-member@example.com": []string{"audience-group"},
+ },
+ })
+
+ Convey("Direct matches and misses", func() {
+ // Match.
+ res, err := cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:requestor@example.com",
+ Delegatee: "user:allowed-to-impersonate@example.com",
+ Audience: makeSet("user:allowed-audience@example.com"),
+ Services: makeSet("service:some-service"),
+ })
+ So(err, ShouldBeNil)
+ So(res, ShouldNotBeNil)
+ So(res.Name, ShouldEqual, "rule 1")
+
+ // Unknown requestor.
+ res, err = cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:unknown-requestor@example.com",
+ Delegatee: "user:allowed-to-impersonate@example.com",
+ Audience: makeSet("user:allowed-audience@example.com"),
+ Services: makeSet("service:some-service"),
+ })
+ So(err, ShouldErrLike, "no matching delegation rules in the config")
+ So(res, ShouldBeNil)
+
+ // Unknown delegatee.
+ res, err = cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:requestor@example.com",
+ Delegatee: "user:unknown-allowed-to-impersonate@example.com",
+ Audience: makeSet("user:allowed-audience@example.com"),
+ Services: makeSet("service:some-service"),
+ })
+ So(err, ShouldErrLike, "no matching delegation rules in the config")
+ So(res, ShouldBeNil)
+
+ // Unknown audience.
+ res, err = cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:requestor@example.com",
+ Delegatee: "user:allowed-to-impersonate@example.com",
+ Audience: makeSet("user:unknown-allowed-audience@example.com"),
+ Services: makeSet("service:some-service"),
+ })
+ So(err, ShouldErrLike, "no matching delegation rules in the config")
+ So(res, ShouldBeNil)
+
+ // Unknown target service.
+ res, err = cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:requestor@example.com",
+ Delegatee: "user:allowed-to-impersonate@example.com",
+ Audience: makeSet("user:allowed-audience@example.com"),
+ Services: makeSet("service:unknown-some-service"),
+ })
+ So(err, ShouldErrLike, "no matching delegation rules in the config")
+ So(res, ShouldBeNil)
+ })
+
+ Convey("Matches via groups", func() {
+ res, err := cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:requestor-group-member@example.com",
+ Delegatee: "user:delegatees-group-member@example.com",
+ Audience: makeSet("group:audience-group"),
+ Services: makeSet("service:some-service"),
+ })
+ So(err, ShouldBeNil)
+ So(res, ShouldNotBeNil)
+ So(res.Name, ShouldEqual, "rule 2")
+
+ // Doesn't do group lookup when checking audience!
+ res, err = cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:requestor-group-member@example.com",
+ Delegatee: "user:delegatees-group-member@example.com",
+ Audience: makeSet("user:audience-group-member@example.com"),
+ Services: makeSet("service:some-service"),
+ })
+ So(err, ShouldErrLike, "no matching delegation rules in the config")
+ So(res, ShouldBeNil)
+ })
+
+ Convey("REQUESTOR rules work", func() {
+ res, err := cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:requestor-group-member@example.com",
+ Delegatee: "user:requestor-group-member@example.com",
+ Audience: makeSet("user:requestor-group-member@example.com"),
+ Services: makeSet("service:some-service"),
+ })
+ So(err, ShouldBeNil)
+ So(res, ShouldNotBeNil)
+ So(res.Name, ShouldEqual, "rule 3")
+ })
+
+ Convey("'*' rules work", func() {
+ res, err := cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:some-requestor@example.com",
+ Delegatee: "user:some-requestor@example.com",
+ Audience: makeSet("group:abc", "user:def@example.com"),
+ Services: makeSet("service:unknown"),
+ })
+ So(err, ShouldBeNil)
+ So(res, ShouldNotBeNil)
+ So(res.Name, ShouldEqual, "rule 4")
+ })
+
+ Convey("a conflict is handled", func() {
+ res, err := cfg.FindMatchingRule(ctx, &RulesQuery{
+ Requestor: "user:conflicts-with-rule-5@example.com",
+ Delegatee: "user:conflicts-with-rule-5@example.com",
+ Audience: makeSet("group:abc", "user:def@example.com"),
+ Services: makeSet("service:unknown"),
+ })
+ So(err, ShouldErrLike, `ambiguous request, multiple delegation rules match ("rule 4", "rule 5")`)
+ So(res, ShouldBeNil)
+ })
+ })
+}
+
+func loadConfig(text string) (*DelegationConfig, error) {
+ cfg := &admin.DelegationPermissions{}
+ err := proto.UnmarshalText(text, cfg)
+ if err != nil {
+ return nil, err
+ }
+ blob, err := proto.Marshal(cfg)
+ if err != nil {
+ return nil, err
+ }
+ c := &DelegationConfig{Config: blob}
+ if err := c.Initialize(); err != nil {
+ return nil, err
+ }
+ return c, nil
+}
+
+func makeSet(ident ...string) *identityset.Set {
+ s, err := identityset.FromStrings(ident, nil)
+ if err != nil {
+ panic(err)
+ }
+ return s
+}
« no previous file with comments | « tokenserver/appengine/delegation/config.go ('k') | tokenserver/appengine/delegation/rpc_import_delegation_configs.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698