veyron/security/caveat: Move common caveats out of veyron/runtimes/google
to the veyron/security/caveat package.
These caveat implementations are generic and do not depend on the Google
runtime. While at it, also added unittests for these caveats.
Change-Id: I5dd70dc6292148b778d9b77859a6d174790def0c
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 4784673..4260de9 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -19,7 +19,7 @@
"veyron/runtimes/google/ipc/version"
inaming "veyron/runtimes/google/naming"
isecurity "veyron/runtimes/google/security"
- icaveat "veyron/runtimes/google/security/caveat"
+ "veyron/security/caveat"
"veyron2"
"veyron2/ipc"
@@ -298,11 +298,11 @@
authorizeErr := "has one or more invalid caveats"
nameErr := "does not have a name matching the provided pattern"
- cavOnlyV1 := security.UniversalCaveat(&icaveat.PeerIdentity{Peers: []security.PrincipalPattern{"client/v1"}})
+ cavOnlyV1 := security.UniversalCaveat(caveat.PeerIdentity{"client/v1"})
now := time.Now()
cavExpired := security.ServiceCaveat{
Service: security.AllPrincipals,
- Caveat: &icaveat.Expiry{IssueTime: now, ExpiryTime: now},
+ Caveat: &caveat.Expiry{IssueTime: now, ExpiryTime: now},
}
clientV1ID := derive(clientID, "v1")
@@ -419,12 +419,12 @@
func TestRPCAuthorization(t *testing.T) {
cavOnlyEcho := security.ServiceCaveat{
Service: security.AllPrincipals,
- Caveat: &icaveat.MethodRestriction{[]string{"Echo"}},
+ Caveat: caveat.MethodRestriction{"Echo"},
}
now := time.Now()
cavExpired := security.ServiceCaveat{
Service: security.AllPrincipals,
- Caveat: &icaveat.Expiry{IssueTime: now, ExpiryTime: now},
+ Caveat: &caveat.Expiry{IssueTime: now, ExpiryTime: now},
}
blessedByServerOnlyEcho := derive(serverID, "onlyEcho", cavOnlyEcho)
@@ -436,7 +436,7 @@
aclAuthErr = "no matching ACL entry found"
)
invalidMethodErr := func(method string) string {
- return fmt.Sprintf("caveat.MethodRestriction{Methods:[Echo]} forbids invocation of method %s", method)
+ return fmt.Sprintf(`caveat.MethodRestriction{"Echo"} forbids invocation of method %s`, method)
}
type v []interface{}
diff --git a/runtimes/google/security/caveat/caveat.go b/runtimes/google/security/caveat/caveat.go
index 11f9595..b589811 100644
--- a/runtimes/google/security/caveat/caveat.go
+++ b/runtimes/google/security/caveat/caveat.go
@@ -1,89 +1,2 @@
-// Package caveat provides a collection of standard caveats that can be validated
-// by the framework. All these caveats implement the security.Caveat interface.
+// Package caveat provides some third-party caveat implementations for the Google runtime.
package caveat
-
-import (
- "fmt"
- "time"
-
- "veyron2/security"
- "veyron2/vom"
-)
-
-// Expiry implements security.Caveat. It specifies a restriction
-// on the validity period of the credential that bears the caveat.
-type Expiry struct {
- IssueTime time.Time
- ExpiryTime time.Time
-}
-
-func (c *Expiry) String() string {
- return fmt.Sprintf("%T%+v", *c, *c)
-}
-
-// Validate checks that the current time is within the IssueTime and ExpiryTime
-// specified by the caveat.
-func (c *Expiry) Validate(context security.Context) error {
- now := time.Now()
- if now.Before(c.IssueTime) || now.After(c.ExpiryTime) {
- return fmt.Errorf("ExpiryCaveat %v forbids credential from being used at this time(%v)", c, now)
- }
- return nil
-}
-
-// MethodRestriction implements security.Caveat. It restricts the methods
-// that can be invoked using a credential bearing the caveat to the set
-// of methods specified on the caveat. An empty set indicates that no method
-// can be invoked.
-type MethodRestriction struct {
- Methods []string
-}
-
-func (c *MethodRestriction) String() string {
- return fmt.Sprintf("%T%+v", *c, *c)
-}
-
-// Validate checks that if a method is being invoked then its name is present on the set
-// specified on the caveat.
-func (c *MethodRestriction) Validate(ctx security.Context) error {
- // If the context has an empty Method then the caveat validates.
- if ctx.Method() == "" {
- return nil
- }
- for _, m := range c.Methods {
- if m == ctx.Method() {
- return nil
- }
- }
- return fmt.Errorf("%v forbids invocation of method %s", c, ctx.Method())
-}
-
-// PeerIdentity implements security.Caveat. A credential bearing this caveat
-// can be used to make and receive RPCs from only those peers whose identities
-// match one of the provided security.PrincipalPatterns. If no
-// security.PrincipalPatterns are provided then communication with all peers is
-// forbidden.
-type PeerIdentity struct {
- Peers []security.PrincipalPattern
-}
-
-func (c *PeerIdentity) String() string {
- return fmt.Sprintf("%T%+v", *c, *c)
-}
-
-// Validate checks that the identity of the peer is present on the set of services
-// identified by the PrincipalPatterns on the caveat.
-func (c *PeerIdentity) Validate(ctx security.Context) error {
- for _, p := range c.Peers {
- if ctx.LocalID() != nil && ctx.LocalID().Match(p) {
- return nil
- }
- }
- return fmt.Errorf("%v forbids RPCing with peer %s", c, ctx.LocalID())
-}
-
-func init() {
- vom.Register(Expiry{})
- vom.Register(MethodRestriction{})
- vom.Register(PeerIdentity{})
-}
diff --git a/runtimes/google/security/caveat/util.go b/runtimes/google/security/caveat/util.go
index 7f8ad4e..723067a 100644
--- a/runtimes/google/security/caveat/util.go
+++ b/runtimes/google/security/caveat/util.go
@@ -9,6 +9,7 @@
"time"
"veyron/runtimes/google/security/wire"
+ "veyron/security/caveat"
"veyron2/security"
)
@@ -47,7 +48,7 @@
}
now := time.Now()
- expiryCaveat := &Expiry{IssueTime: now, ExpiryTime: now.Add(duration)}
+ expiryCaveat := &caveat.Expiry{IssueTime: now, ExpiryTime: now.Add(duration)}
caveats = append(caveats, security.UniversalCaveat(expiryCaveat))
encodedCaveats, err := wire.EncodeCaveats(caveats)
diff --git a/runtimes/google/security/identity_chain.go b/runtimes/google/security/identity_chain.go
index 2f62091..849e795 100644
--- a/runtimes/google/security/identity_chain.go
+++ b/runtimes/google/security/identity_chain.go
@@ -11,9 +11,10 @@
"reflect"
"time"
- "veyron/runtimes/google/security/caveat"
+ icaveat "veyron/runtimes/google/security/caveat"
"veyron/runtimes/google/security/keys"
"veyron/runtimes/google/security/wire"
+ "veyron/security/caveat"
"veyron2/security"
"veyron2/vom"
@@ -196,8 +197,8 @@
func (id *chainPrivateID) MintDischarge(cav security.ThirdPartyCaveat, duration time.Duration, dischargeCaveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
switch c := cav.(type) {
- case *caveat.PublicKeyCaveat:
- return caveat.NewPublicKeyDischarge(c, id.privateKey, duration, dischargeCaveats)
+ case *icaveat.PublicKeyCaveat:
+ return icaveat.NewPublicKeyDischarge(c, id.privateKey, duration, dischargeCaveats)
}
return nil, fmt.Errorf("discharge cannot be constructed for ThirdPartyCaveat of type %T from PrivateID of type %T", cav, id)
}
diff --git a/runtimes/google/security/identity_test.go b/runtimes/google/security/identity_test.go
index 2011a93..a1bad90 100644
--- a/runtimes/google/security/identity_test.go
+++ b/runtimes/google/security/identity_test.go
@@ -327,7 +327,7 @@
tests: []rpc{
{server: bob, method: "Hello", authName: "veyron/alice"},
{server: bob, authName: "veyron/alice"},
- {server: googleTree.PublicID(), method: "Hello", authErr: "caveat.MethodRestriction{Methods:[Play]} forbids invocation of method Hello"},
+ {server: googleTree.PublicID(), method: "Hello", authErr: `caveat.MethodRestriction{"Play"} forbids invocation of method Hello`},
{server: googleTree.PublicID(), method: "Play", authName: "veyron/alice"},
{server: googleTree.PublicID(), authName: "veyron/alice"},
},
@@ -335,7 +335,7 @@
{
client: bless(cAlice, veyronChain, "alice", cavOnlyGoogle),
tests: []rpc{
- {server: bob, method: "Hello", authErr: "caveat.PeerIdentity{Peers:[google]} forbids RPCing with peer untrusted/bob"},
+ {server: bob, method: "Hello", authErr: `caveat.PeerIdentity{"google"} forbids RPCing with peer untrusted/bob`},
{server: googleTree.PublicID(), method: "Hello", authName: "veyron/alice"},
{server: googleTree.PublicID(), method: "Play", authName: "veyron/alice"},
},
@@ -343,15 +343,15 @@
{
client: bless(cAlice, veyronChain, "alice", append(cavOnlyGoogle, cavOnlyPlayAtGoogle...)),
tests: []rpc{
- {server: bob, method: "Hello", authErr: "caveat.PeerIdentity{Peers:[google]} forbids RPCing with peer untrusted/bob"},
- {server: googleTree.PublicID(), method: "Hello", authErr: "caveat.MethodRestriction{Methods:[Play]} forbids invocation of method Hello"},
+ {server: bob, method: "Hello", authErr: `caveat.PeerIdentity{"google"} forbids RPCing with peer untrusted/bob`},
+ {server: googleTree.PublicID(), method: "Hello", authErr: `caveat.MethodRestriction{"Play"} forbids invocation of method Hello`},
{server: googleTree.PublicID(), method: "Play", authName: "veyron/alice"},
},
},
{
client: bless(cAlice, veyronChain, "alice", cavOnlyPublicProfile),
tests: []rpc{
- {server: cVeyronAliceTV, method: "PrivateProfile", authErr: "caveat.MethodRestriction{Methods:[PublicProfile]} forbids invocation of method PrivateProfile"},
+ {server: cVeyronAliceTV, method: "PrivateProfile", authErr: `caveat.MethodRestriction{"PublicProfile"} forbids invocation of method PrivateProfile`},
{server: cVeyronAliceTV, method: "PublicProfile", authName: "veyron/alice"},
},
},
diff --git a/runtimes/google/security/identity_tree.go b/runtimes/google/security/identity_tree.go
index 80b6930..4425759 100644
--- a/runtimes/google/security/identity_tree.go
+++ b/runtimes/google/security/identity_tree.go
@@ -13,9 +13,10 @@
"strings"
"time"
- "veyron/runtimes/google/security/caveat"
+ icaveat "veyron/runtimes/google/security/caveat"
"veyron/runtimes/google/security/keys"
"veyron/runtimes/google/security/wire"
+ "veyron/security/caveat"
"veyron2/security"
"veyron2/vom"
)
@@ -232,8 +233,8 @@
func (id *treePrivateID) MintDischarge(cav security.ThirdPartyCaveat, duration time.Duration, dischargeCaveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
switch c := cav.(type) {
- case *caveat.PublicKeyCaveat:
- return caveat.NewPublicKeyDischarge(c, id.privateKey, duration, dischargeCaveats)
+ case *icaveat.PublicKeyCaveat:
+ return icaveat.NewPublicKeyDischarge(c, id.privateKey, duration, dischargeCaveats)
}
return nil, fmt.Errorf("discharge cannot be constructed for ThirdPartyCaveat of type %T from PrivateID of type %T", cav, id)
}
diff --git a/runtimes/google/security/util_test.go b/runtimes/google/security/util_test.go
index 15955bc..667fad6 100644
--- a/runtimes/google/security/util_test.go
+++ b/runtimes/google/security/util_test.go
@@ -8,8 +8,8 @@
"testing"
"time"
- "veyron/runtimes/google/security/caveat"
"veyron/runtimes/google/security/keys"
+ "veyron/security/caveat"
"veyron2/security"
"veyron2/vom"
@@ -104,14 +104,12 @@
func methodRestrictionCaveat(service security.PrincipalPattern, methods []string) []security.ServiceCaveat {
return []security.ServiceCaveat{
- {Service: service, Caveat: &caveat.MethodRestriction{Methods: methods}},
+ {Service: service, Caveat: caveat.MethodRestriction(methods)},
}
}
func peerIdentityCaveat(p security.PrincipalPattern) []security.ServiceCaveat {
- return []security.ServiceCaveat{
- security.UniversalCaveat(&caveat.PeerIdentity{Peers: []security.PrincipalPattern{p}}),
- }
+ return []security.ServiceCaveat{security.UniversalCaveat(caveat.PeerIdentity{p})}
}
func init() {
diff --git a/security/caveat/caveat.go b/security/caveat/caveat.go
new file mode 100644
index 0000000..6cb4735
--- /dev/null
+++ b/security/caveat/caveat.go
@@ -0,0 +1,66 @@
+// Package caveat provides common security.Caveat implementations.
+package caveat
+
+import (
+ "fmt"
+ "time"
+
+ "veyron2/security"
+ "veyron2/vom"
+)
+
+// Expiry is a security.Caveat that restricts the validity period of
+// the credential bearing this caveat.
+type Expiry struct {
+ IssueTime time.Time
+ ExpiryTime time.Time
+}
+
+func (c *Expiry) Validate(context security.Context) error {
+ now := time.Now()
+ if now.Before(c.IssueTime) || now.After(c.ExpiryTime) {
+ return fmt.Errorf("%#v forbids credential from being used at this time(%v)", c, now)
+ }
+ return nil
+}
+
+// MethodRestriction is a security.Caveat that restricts the set of
+// methods that can be invoked by a credential bearing the caveat.
+// An empty set indicates that no methods can be invoked.
+type MethodRestriction []string
+
+func (c MethodRestriction) Validate(ctx security.Context) error {
+ // If the context has an empty Method then the caveat validates.
+ if ctx.Method() == "" {
+ return nil
+ }
+ for _, m := range c {
+ if m == ctx.Method() {
+ return nil
+ }
+ }
+ return fmt.Errorf("%#v forbids invocation of method %s", c, ctx.Method())
+}
+
+// PeerIdentity is a security.Caveat that restricts the bearer of a credential
+// with this caveat from making or receiving RPCs to a limited set of peers -
+// those whose identities match one of the provided security.PrincipalPatterns.
+// An empty set indicates that no peers can be communicated with.
+type PeerIdentity []security.PrincipalPattern
+
+// Validate checks that the identity of the peer is present on the set of services
+// identified by the PrincipalPatterns on the caveat.
+func (c PeerIdentity) Validate(ctx security.Context) error {
+ for _, p := range c {
+ if ctx.LocalID() != nil && ctx.LocalID().Match(p) {
+ return nil
+ }
+ }
+ return fmt.Errorf("%#v forbids RPCing with peer %s", c, ctx.LocalID())
+}
+
+func init() {
+ vom.Register(Expiry{})
+ vom.Register(MethodRestriction(nil))
+ vom.Register(PeerIdentity(nil))
+}
diff --git a/security/caveat/caveat_test.go b/security/caveat/caveat_test.go
new file mode 100644
index 0000000..8c7efdf
--- /dev/null
+++ b/security/caveat/caveat_test.go
@@ -0,0 +1,50 @@
+package caveat_test
+
+import (
+ "testing"
+ "time"
+
+ "veyron/security/caveat"
+ "veyron2/security"
+)
+
+type context struct {
+ local, remote security.PublicID
+ method string
+}
+
+func (c *context) Method() string { return c.method }
+func (c *context) Name() string { return "some_name" }
+func (c *context) Suffix() string { return "some_suffix" }
+func (c *context) Label() security.Label { return security.AdminLabel }
+func (c *context) CaveatDischarges() security.CaveatDischargeMap { return nil }
+func (c *context) LocalID() security.PublicID { return c.local }
+func (c *context) RemoteID() security.PublicID { return c.remote }
+
+func TestCaveats(t *testing.T) {
+ var (
+ alice = security.FakePublicID("alice")
+ bob = security.FakePublicID("bob")
+ )
+ now := time.Now()
+ tests := []struct {
+ c security.Caveat
+ ok bool
+ }{
+ {&caveat.Expiry{IssueTime: now, ExpiryTime: now.Add(time.Hour)}, true},
+ {&caveat.Expiry{IssueTime: now.Add(-1 * time.Hour), ExpiryTime: now.Add(-1 * time.Minute)}, false},
+ {caveat.MethodRestriction(nil), false},
+ {caveat.MethodRestriction{"Pause", "Play"}, true},
+ {caveat.MethodRestriction{"List"}, false},
+ {caveat.PeerIdentity(nil), false},
+ {caveat.PeerIdentity{"fake/alice"}, true},
+ {caveat.PeerIdentity{"fake/carol"}, false},
+ {caveat.PeerIdentity{"fake/alice", "fake/carol"}, true},
+ }
+ ctx := &context{local: alice, remote: bob, method: "Play"}
+ for _, test := range tests {
+ if err := test.c.Validate(ctx); test.ok != (err == nil) {
+ t.Errorf("Caveat:%#v. Got error:%v, want error:%v", test.c, err, test.ok)
+ }
+ }
+}
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index 28eb42f..f4a908f 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -19,7 +19,7 @@
)
var (
- errNamingLoop = verror.Make(verror.BadArg, "Loop in namespace")
+ errNamingLoop = verror.Make(verror.BadArg, "Loop in namespace")
)
// mountTable represents a namespace. One exists per server instance.
@@ -200,7 +200,7 @@
// A useful node has children or an active mount.
func (n *node) isUseful() bool {
- return len(n.children) > 0 || n.mount.isActive()
+ return len(n.children) > 0 || n.mount.isActive()
}
// removeUseless removes a node and all of its ascendants that are not useful.