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