Merge "security/access: Add an "access tag" caveat."
diff --git a/security/access/.api b/security/access/.api
index 294a21e..ab5cecd 100644
--- a/security/access/.api
+++ b/security/access/.api
@@ -5,7 +5,9 @@
 pkg access, const Write Tag
 pkg access, func AllTypicalTags() []Tag
 pkg access, func IsUnenforceablePatterns(error) []security.BlessingPattern
+pkg access, func NewAccessTagCaveat(...Tag) (security.Caveat, error)
 pkg access, func NewErrAccessListMatch(*context.T, []string, []security.RejectedBlessing) error
+pkg access, func NewErrAccessTagCaveatValidation(*context.T, []string, []Tag) error
 pkg access, func NewErrInvalidOpenAccessList(*context.T) error
 pkg access, func NewErrNoPermissions(*context.T, []string, []security.RejectedBlessing, string) error
 pkg access, func NewErrTooBig(*context.T) error
@@ -29,7 +31,9 @@
 pkg access, type AccessList struct, NotIn []string
 pkg access, type Permissions map[string]AccessList
 pkg access, type Tag string
+pkg access, var AccessTagCaveat security.CaveatDescriptor
 pkg access, var ErrAccessListMatch verror.IDAction
+pkg access, var ErrAccessTagCaveatValidation verror.IDAction
 pkg access, var ErrInvalidOpenAccessList verror.IDAction
 pkg access, var ErrNoPermissions verror.IDAction
 pkg access, var ErrTooBig verror.IDAction
diff --git a/security/access/caveat.go b/security/access/caveat.go
new file mode 100644
index 0000000..5acb2ca
--- /dev/null
+++ b/security/access/caveat.go
@@ -0,0 +1,38 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package access
+
+import (
+	"v.io/v23/context"
+	"v.io/v23/security"
+	"v.io/v23/vdl"
+)
+
+func init() {
+	security.RegisterCaveatValidator(AccessTagCaveat, func(ctx *context.T, call security.Call, params []Tag) error {
+		wantT := TypicalTagType()
+		methodTags := call.MethodTags()
+		for _, mt := range methodTags {
+			if mt.Type() == wantT {
+				for _, ct := range params {
+					if mt.RawString() == vdl.ValueOf(ct).RawString() {
+						return nil
+					}
+				}
+			}
+		}
+		strs := make([]string, len(methodTags))
+		for i, mt := range methodTags {
+			strs[i] = mt.RawString()
+		}
+		return NewErrAccessTagCaveatValidation(ctx, strs, params)
+	})
+}
+
+// NewAccessTagCaveat returns a Caveat that will validate iff the intersection
+// of the tags on the method being invoked and those in 'tags' is non-empty.
+func NewAccessTagCaveat(tags ...Tag) (security.Caveat, error) {
+	return security.NewCaveat(AccessTagCaveat, tags)
+}
diff --git a/security/access/caveat_test.go b/security/access/caveat_test.go
new file mode 100644
index 0000000..dc7586b
--- /dev/null
+++ b/security/access/caveat_test.go
@@ -0,0 +1,53 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package access_test
+
+import (
+	"testing"
+
+	"v.io/v23/context"
+	"v.io/v23/security"
+	"v.io/v23/security/access"
+	"v.io/v23/vdl"
+)
+
+func TestAccessTagCaveat(t *testing.T) {
+	var (
+		server     = newPrincipal(t)
+		bserver, _ = server.BlessSelf("server")
+		caveat, _  = access.NewAccessTagCaveat(access.Debug, access.Resolve)
+		bclient, _ = server.Bless(newPrincipal(t).PublicKey(), bserver, "debugger", caveat)
+		tests      = []struct {
+			MethodTags []*vdl.Value
+			OK         bool
+		}{
+			{nil, false},
+			{[]*vdl.Value{vdl.ValueOf(access.Debug)}, true},
+			{[]*vdl.Value{vdl.ValueOf(access.Resolve)}, true},
+			{[]*vdl.Value{vdl.ValueOf(access.Read), vdl.ValueOf(access.Debug)}, true},
+			{[]*vdl.Value{vdl.ValueOf(access.Read), vdl.ValueOf(access.Write)}, false},
+			{[]*vdl.Value{vdl.ValueOf("Debug"), vdl.ValueOf("Resolve")}, false},
+		}
+	)
+	security.AddToRoots(server, bserver)
+	ctx, cancel := context.RootContext()
+	defer cancel()
+	for idx, test := range tests {
+		call := security.NewCall(&security.CallParams{
+			MethodTags:      test.MethodTags,
+			LocalPrincipal:  server,
+			RemoteBlessings: bclient,
+		})
+		got, rejected := security.RemoteBlessingNames(ctx, call)
+		if test.OK {
+			if len(got) != 1 || got[0] != "server:debugger" {
+				t.Errorf("Got (%v, %v), wanted ([%q], nil) for method tags %v (test case #%d)", got, rejected, "server:debugger", test.MethodTags, idx)
+			}
+		}
+		if !test.OK && len(got) != 0 {
+			t.Errorf("Got (%v, %v), wanted all blessings to be rejected for method tags %v (test case #%d)", got, rejected, test.MethodTags, idx)
+		}
+	}
+}
diff --git a/security/access/types.vdl b/security/access/types.vdl
index e72bbcf..9405064 100644
--- a/security/access/types.vdl
+++ b/security/access/types.vdl
@@ -100,6 +100,7 @@
 package access
 
 import "v.io/v23/security"
+import "v.io/v23/uniqueid"
 
 // AccessList represents a set of blessings that should be granted access.
 //
@@ -148,6 +149,13 @@
   Read    = Tag("Read")     // Operations that do not mutate the state of the object.
   Write   = Tag("Write")    // Operations that mutate the state of the object.
   Resolve = Tag("Resolve")  // Operations involving namespace navigation.
+
+  // AccessTagCaveat represents a caveat that validates iff the method being invoked has
+  // at least one of the tags listed in the caveat.
+  AccessTagCaveat  = security.CaveatDescriptor{
+    Id:        uniqueid.Id{0xef, 0xcd, 0xe3, 0x75, 0x14, 0x16, 0xc7, 0x3b, 0x18, 0x9c, 0xe8, 0x9c, 0xcc, 0x93, 0x80, 0x0},
+    ParamType: typeobject([]Tag),
+  }
 )
 
 // Note: For "bad version" errors, use verror.ErrBadVersion.
@@ -162,4 +170,8 @@
 	UnenforceablePatterns(rejectedPatterns []security.BlessingPattern){"en":"AccessList contains the following invalid or unrecognized patterns in the In list: {rejectedPatterns}"}
 
 	InvalidOpenAccessList(){"en": "AccessList with the pattern ... in its In list must have no other patterns in the In or NotIn lists"}
+
+	AccessTagCaveatValidation(methodTags []string, caveatTags []Tag){
+		"en": "access tags on method ({methodTags}) do not include any of the ones in the caveat ({caveatTags}), or the method is using a different tag type",
+	}
 )
diff --git a/security/access/types.vdl.go b/security/access/types.vdl.go
index d0133d2..2ce46f5 100644
--- a/security/access/types.vdl.go
+++ b/security/access/types.vdl.go
@@ -111,6 +111,7 @@
 
 	// VDL user imports
 	"v.io/v23/security"
+	"v.io/v23/uniqueid"
 )
 
 // AccessList represents a set of blessings that should be granted access.
@@ -184,13 +185,38 @@
 
 const Resolve = Tag("Resolve") // Operations involving namespace navigation.
 
+// AccessTagCaveat represents a caveat that validates iff the method being invoked has
+// at least one of the tags listed in the caveat.
+var AccessTagCaveat = security.CaveatDescriptor{
+	Id: uniqueid.Id{
+		239,
+		205,
+		227,
+		117,
+		20,
+		22,
+		199,
+		59,
+		24,
+		156,
+		232,
+		156,
+		204,
+		147,
+		128,
+		0,
+	},
+	ParamType: vdl.TypeOf([]Tag(nil)),
+}
+
 var (
 	// The AccessList is too big.  Use groups to represent large sets of principals.
-	ErrTooBig                = verror.Register("v.io/v23/security/access.TooBig", verror.NoRetry, "{1:}{2:} AccessList is too big")
-	ErrNoPermissions         = verror.Register("v.io/v23/security/access.NoPermissions", verror.NoRetry, "{1:}{2:} {3} does not have {5} access (rejected blessings: {4})")
-	ErrAccessListMatch       = verror.Register("v.io/v23/security/access.AccessListMatch", verror.NoRetry, "{1:}{2:} {3} does not match the access list (rejected blessings: {4})")
-	ErrUnenforceablePatterns = verror.Register("v.io/v23/security/access.UnenforceablePatterns", verror.NoRetry, "{1:}{2:} AccessList contains the following invalid or unrecognized patterns in the In list: {3}")
-	ErrInvalidOpenAccessList = verror.Register("v.io/v23/security/access.InvalidOpenAccessList", verror.NoRetry, "{1:}{2:} AccessList with the pattern ... in its In list must have no other patterns in the In or NotIn lists")
+	ErrTooBig                    = verror.Register("v.io/v23/security/access.TooBig", verror.NoRetry, "{1:}{2:} AccessList is too big")
+	ErrNoPermissions             = verror.Register("v.io/v23/security/access.NoPermissions", verror.NoRetry, "{1:}{2:} {3} does not have {5} access (rejected blessings: {4})")
+	ErrAccessListMatch           = verror.Register("v.io/v23/security/access.AccessListMatch", verror.NoRetry, "{1:}{2:} {3} does not match the access list (rejected blessings: {4})")
+	ErrUnenforceablePatterns     = verror.Register("v.io/v23/security/access.UnenforceablePatterns", verror.NoRetry, "{1:}{2:} AccessList contains the following invalid or unrecognized patterns in the In list: {3}")
+	ErrInvalidOpenAccessList     = verror.Register("v.io/v23/security/access.InvalidOpenAccessList", verror.NoRetry, "{1:}{2:} AccessList with the pattern ... in its In list must have no other patterns in the In or NotIn lists")
+	ErrAccessTagCaveatValidation = verror.Register("v.io/v23/security/access.AccessTagCaveatValidation", verror.NoRetry, "{1:}{2:} access tags on method ({3}) do not include any of the ones in the caveat ({4}), or the method is using a different tag type")
 )
 
 func init() {
@@ -199,6 +225,7 @@
 	i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrAccessListMatch.ID), "{1:}{2:} {3} does not match the access list (rejected blessings: {4})")
 	i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrUnenforceablePatterns.ID), "{1:}{2:} AccessList contains the following invalid or unrecognized patterns in the In list: {3}")
 	i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrInvalidOpenAccessList.ID), "{1:}{2:} AccessList with the pattern ... in its In list must have no other patterns in the In or NotIn lists")
+	i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrAccessTagCaveatValidation.ID), "{1:}{2:} access tags on method ({3}) do not include any of the ones in the caveat ({4}), or the method is using a different tag type")
 }
 
 // NewErrTooBig returns an error with the ErrTooBig ID.
@@ -225,3 +252,8 @@
 func NewErrInvalidOpenAccessList(ctx *context.T) error {
 	return verror.New(ErrInvalidOpenAccessList, ctx)
 }
+
+// NewErrAccessTagCaveatValidation returns an error with the ErrAccessTagCaveatValidation ID.
+func NewErrAccessTagCaveatValidation(ctx *context.T, methodTags []string, caveatTags []Tag) error {
+	return verror.New(ErrAccessTagCaveatValidation, ctx, methodTags, caveatTags)
+}
diff --git a/security/caveat.go b/security/caveat.go
index dae59b6..2603154 100644
--- a/security/caveat.go
+++ b/security/caveat.go
@@ -221,21 +221,13 @@
 
 // NewExpiryCaveat returns a Caveat that validates iff the current time is before t.
 func NewExpiryCaveat(t time.Time) (Caveat, error) {
-	c, err := NewCaveat(ExpiryCaveat, t)
-	if err != nil {
-		return c, err
-	}
-	return c, nil
+	return NewCaveat(ExpiryCaveat, t)
 }
 
 // NewMethodCaveat returns a Caveat that validates iff the method being invoked by
 // the peer is listed in an argument to this function.
 func NewMethodCaveat(method string, additionalMethods ...string) (Caveat, error) {
-	c, err := NewCaveat(MethodCaveat, append(additionalMethods, method))
-	if err != nil {
-		return c, err
-	}
-	return c, nil
+	return NewCaveat(MethodCaveat, append(additionalMethods, method))
 }
 
 // NewPublicKeyCaveat returns a third-party caveat, i.e., the returned