"physical-lock": listkeys
This CL makes two changes:
1) Streamlines the naming of lock devices. Lock devices are always
named by the name using which they were claimed. This is also the
name that the device authenticates as and a prefix of every key for
the lock.
2) Adds 'listkeys' command to the command-line client that lists
the set of keys bound to the principal invoking the command and the
and the locks to the which they apply.
Change-Id: If3e8bbef4efdec910ebd69b7184478b26fbac229
diff --git a/go/src/v.io/x/lock/lock/main.go b/go/src/v.io/x/lock/lock/main.go
index 2ea6e82..f5e1a29 100644
--- a/go/src/v.io/x/lock/lock/main.go
+++ b/go/src/v.io/x/lock/lock/main.go
@@ -9,6 +9,7 @@
"io/ioutil"
"os"
"path"
+ "strings"
"time"
"v.io/v23"
@@ -25,13 +26,13 @@
)
var (
- lockGlobPattern = path.Join("nh", locklib.LockNhPrefix+"*")
- cmdScan = &cmdline.Command{
+ lockNhNamePrefix = path.Join("nh", locklib.LockNhPrefix)
+ cmdScan = &cmdline.Command{
Runner: v23cmd.RunnerFunc(runScan),
Name: "scan",
- Short: "Scan the neighborhood for lock objects",
+ Short: "Scan the neighborhood for lock devices",
Long: `
-Globs over the neighborhood to find names of lock objects (both claimed
+Globs over the neighborhood to find lock devices (both claimed
and unclaimed).
`,
}
@@ -41,11 +42,11 @@
Short: "Claim the specified lock with the provided name",
Long: `
Claims the specified unclaimed lock with the provided name, and authorizes the
-principal executing this command to access the claimed lock object.
+principal executing this command to access the claimed lock.
`,
ArgsName: "<lock> <name>",
ArgsLong: `
-<lock> is the object name of the unclaimed lock.
+<lock> is the name of the unclaimed lock.
<name> is a name that you'd like to give to the lock, for example,
"my_front_door" or "123_main_street.
`,
@@ -59,7 +60,7 @@
`,
ArgsName: "<lock>",
ArgsLong: `
-<lock> is the object name of the lock.
+<lock> is the name of the lock.
`,
}
cmdUnlock = &cmdline.Command{
@@ -71,7 +72,7 @@
`,
ArgsName: "<lock>",
ArgsLong: `
-<lock> is the object name of the lock.
+<lock> is the name of the lock.
`,
}
cmdStatus = &cmdline.Command{
@@ -83,7 +84,22 @@
`,
ArgsName: "<lock>",
ArgsLong: `
-<lock> is the object name of the lock.
+<lock> is the name of the lock.
+`,
+ }
+ cmdListKeys = &cmdline.Command{
+ Runner: v23cmd.RunnerFunc(runListKeys),
+ Name: "listkeys",
+ Short: "List the set of available keys",
+ Long: `
+Lists the set of available physical-lock keys and the names of the locks
+to which they apply.
+
+Each line of the list is of the form
+<lock name> <key>
+
+TODO(ataly, ashankar): Also print additional information such as when and
+from whom was the key obtained.
`,
}
)
@@ -95,6 +111,7 @@
}
defer stop()
+ lockGlobPattern := lockNhNamePrefix + "*"
locksFound := make(map[string]bool)
fmt.Println("Scanning for Locks...")
for {
@@ -113,23 +130,31 @@
}
locksFound[name] = true
- if bn := ep.BlessingNames(); len(bn) != 0 {
- fmt.Printf("%v [owned by %v]\n", name, bn)
- } else {
- fmt.Printf("%v\n", name)
- }
+ printLockName(name, ep.BlessingNames())
}
}
}
}
}
+func printLockName(name string, blessings []string) {
+ if !strings.HasPrefix(name, lockNhNamePrefix) {
+ return
+ }
+ lockName := strings.TrimPrefix(name, lockNhNamePrefix)
+
+ if len(blessings) != 0 {
+ fmt.Printf("%v [owned by %v]\n", lockName, blessings)
+ } else {
+ fmt.Printf("%v\n", lockName)
+ }
+}
+
func runClaim(ctx *context.T, env *cmdline.Env, args []string) error {
if numargs := len(args); numargs != 2 {
return fmt.Errorf("requires exactly two arguments <lock>, <name>, provided %d", numargs)
}
- lockname, name := args[0], args[1]
- lockname = path.Join(lockname, locklib.LockSuffix)
+ lockName, name := args[0], args[1]
ctx, stop, err := withLocalNamespace(ctx)
if err != nil {
@@ -142,7 +167,7 @@
// TODO(ataly): We should not skip server endpoint authorization while
// claiming locks but instead fetch the blessing root of the lock manufacturer
// from an authoritative source and then appropriately authenticate the server.
- b, err := lock.UnclaimedLockClient(lockname).Claim(ctx, name, options.SkipServerEndpointAuthorization{})
+ b, err := lock.UnclaimedLockClient(lockObjName(lockName)).Claim(ctx, name, options.SkipServerEndpointAuthorization{})
if err != nil {
return err
}
@@ -154,7 +179,7 @@
if _, err := p.BlessingStore().Set(b, security.BlessingPattern(name)); err != nil {
return fmt.Errorf("failed to set (key) blessing (%v) for peer %v: %v", b, name, err)
}
- fmt.Printf("Claimed lock and received key: %v\n", b)
+ fmt.Printf("Claimed lock: %v and received key: %v\n", lockName, b)
return nil
}
@@ -170,8 +195,7 @@
if numargs := len(args); numargs != 1 {
return fmt.Errorf("requires exactly one arguments <lock>, provided %d", numargs)
}
- lockname := args[0]
- lockname = path.Join(lockname, locklib.LockSuffix)
+ lockName := args[0]
ctx, stop, err := withLocalNamespace(ctx)
if err != nil {
@@ -182,15 +206,15 @@
ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel()
if status == lock.Locked {
- err = lock.LockClient(lockname).Lock(ctx)
+ err = lock.LockClient(lockObjName(lockName)).Lock(ctx)
} else {
- err = lock.LockClient(lockname).Unlock(ctx)
+ err = lock.LockClient(lockObjName(lockName)).Unlock(ctx)
}
if err != nil {
return err
}
- fmt.Printf("Updated lock %v to status: %v\n", lockname, status)
+ fmt.Printf("Updated lock %v to status: %v\n", lockName, status)
return nil
}
@@ -198,8 +222,7 @@
if numargs := len(args); numargs != 1 {
return fmt.Errorf("requires exactly one arguments <lock>, provided %d", numargs)
}
- lockname := args[0]
- lockname = path.Join(lockname, locklib.LockSuffix)
+ lockName := args[0]
ctx, stop, err := withLocalNamespace(ctx)
if err != nil {
@@ -209,11 +232,28 @@
ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel()
- status, err := lock.LockClient(lockname).Status(ctx)
+ status, err := lock.LockClient(lockObjName(lockName)).Status(ctx)
if err != nil {
return err
}
- fmt.Printf("lock %v is: %v\n", lockname, status)
+ fmt.Printf("lock %v is: %v\n", lockName, status)
+ return nil
+}
+
+func runListKeys(ctx *context.T, env *cmdline.Env, args []string) error {
+ peerBlessings := v23.GetPrincipal(ctx).BlessingStore().PeerBlessings()
+ const format = "%-30s %s\n"
+
+ fmt.Printf(format, "Lock", "Key")
+ for lock, key := range peerBlessings {
+ if !isValidLockName(string(lock)) {
+ continue
+ }
+ if !isKeyValidForLock(ctx, key, string(lock)) {
+ continue
+ }
+ fmt.Printf(format, lock, key)
+ }
return nil
}
@@ -253,15 +293,36 @@
return ctx, stop, nil
}
+func lockObjName(lockName string) string {
+ return path.Join(lockNhNamePrefix+lockName, locklib.LockSuffix)
+}
+
+func isValidLockName(lock string) bool {
+ // TODO(ataly): HACK!! We should either store the set of valid names
+ // in a file that is managed by this client or somehow note in the
+ // blessing store whether a peer pattern is the name of a lock object.
+ return lock != string(security.AllPrincipals) && !strings.ContainsAny(string(lock), security.ChainSeparator)
+}
+
+func isKeyValidForLock(ctx *context.T, key security.Blessings, lock string) bool {
+ bp := security.BlessingPattern(lock + security.ChainSeparator + "key")
+ for b, _ := range v23.GetPrincipal(ctx).BlessingsInfo(key) {
+ if bp.MatchedBy(b) {
+ return true
+ }
+ }
+ return false
+}
+
func main() {
cmdline.HideGlobalFlagsExcept()
root := &cmdline.Command{
Name: "lock",
Short: "claim and manage locks",
Long: `
-Command lock claims and manages lock objects.
+Command lock claims and manages lock devices.
`,
- Children: []*cmdline.Command{cmdScan, cmdClaim, cmdLock, cmdUnlock, cmdStatus},
+ Children: []*cmdline.Command{cmdScan, cmdClaim, cmdLock, cmdUnlock, cmdStatus, cmdListKeys},
}
cmdline.Main(root)
}
diff --git a/go/src/v.io/x/lock/lockd/errors.vdl b/go/src/v.io/x/lock/lockd/errors.vdl
index 06d77ed..5c47350 100644
--- a/go/src/v.io/x/lock/lockd/errors.vdl
+++ b/go/src/v.io/x/lock/lockd/errors.vdl
@@ -8,4 +8,7 @@
LockAlreadyClaimed() {
"en": "lock has already been claimed",
}
+ InvalidLockName(name, reason string) {
+ "en": "invalid lock name ({name}: cannot contain {reason})",
+ }
)
\ No newline at end of file
diff --git a/go/src/v.io/x/lock/lockd/errors.vdl.go b/go/src/v.io/x/lock/lockd/errors.vdl.go
index 3325811..064e566 100644
--- a/go/src/v.io/x/lock/lockd/errors.vdl.go
+++ b/go/src/v.io/x/lock/lockd/errors.vdl.go
@@ -16,13 +16,20 @@
var (
ErrLockAlreadyClaimed = verror.Register("v.io/x/lock/lockd.LockAlreadyClaimed", verror.NoRetry, "{1:}{2:} lock has already been claimed")
+ ErrInvalidLockName = verror.Register("v.io/x/lock/lockd.InvalidLockName", verror.NoRetry, "{1:}{2:} invalid lock name ({3}: cannot contain {4})")
)
func init() {
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrLockAlreadyClaimed.ID), "{1:}{2:} lock has already been claimed")
+ i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrInvalidLockName.ID), "{1:}{2:} invalid lock name ({3}: cannot contain {4})")
}
// NewErrLockAlreadyClaimed returns an error with the ErrLockAlreadyClaimed ID.
func NewErrLockAlreadyClaimed(ctx *context.T) error {
return verror.New(ErrLockAlreadyClaimed, ctx)
}
+
+// NewErrInvalidLockName returns an error with the ErrInvalidLockName ID.
+func NewErrInvalidLockName(ctx *context.T, name string, reason string) error {
+ return verror.New(ErrInvalidLockName, ctx, name, reason)
+}
diff --git a/go/src/v.io/x/lock/lockd/unclaimed_lock.go b/go/src/v.io/x/lock/lockd/unclaimed_lock.go
index 1f9dd06..8801a9e 100644
--- a/go/src/v.io/x/lock/lockd/unclaimed_lock.go
+++ b/go/src/v.io/x/lock/lockd/unclaimed_lock.go
@@ -7,6 +7,7 @@
import (
"os"
"path/filepath"
+ "strings"
"sync"
"v.io/v23"
@@ -34,6 +35,14 @@
func (ul *unclaimedLock) Claim(ctx *context.T, call rpc.ServerCall, name string) (security.Blessings, error) {
vlog.Infof("Claim called by %q", call.Security().RemoteBlessings())
+ if strings.ContainsAny(name, security.ChainSeparator) {
+ // TODO(ataly, ashankar): We have to error out in this case because of the current
+ // neighborhood setup wherein the neighborhood-name of a claimed lock's mounttable is
+ // the same as the locks's name. Since neighborhood-names aren't allowed to contain
+ // slashes, we have to disallow slashes in the lock name as well.
+ return security.Blessings{}, NewErrInvalidLockName(ctx, name, security.ChainSeparator)
+ }
+
var (
principal = v23.GetPrincipal(ctx)
origDefault = principal.BlessingStore().Default()