blob: 69be3c55b569ad33f8c44fb86bfa943ceaae3771 [file] [log] [blame]
// 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 util
import (
"strconv"
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/verror"
"v.io/v23/vom"
)
func FormatVersion(version uint64) string {
return strconv.FormatUint(version, 10)
}
func CheckVersion(ctx *context.T, presented string, actual uint64) error {
if presented != "" && presented != FormatVersion(actual) {
return verror.NewErrBadVersion(ctx)
}
return nil
}
////////////////////////////////////////////////////////////
// RPC-aware, higher-level get/put
type Layer interface {
// Name returns the name of this instance, e.g. "fooapp" or "bardb".
Name() string
// StKey returns the storage engine key to use for metadata about this layer,
// e.g. "$table:baztable".
StKey() string
}
type Permser interface {
// GetPerms returns the Permissions for this Layer.
GetPerms() access.Permissions
}
// GetWithoutAuth does st.Get(l.StKey(), v), populating v.
// Returns a VDL-compatible error.
func GetWithoutAuth(ctx *context.T, call rpc.ServerCall, st store.StoreReader, l Layer, v interface{}) error {
if err := GetObject(st, l.StKey(), v); err != nil {
if verror.ErrorID(err) == store.ErrUnknownKey.ID {
// TODO(sadovsky): Return ErrNoExist if appropriate.
return verror.New(verror.ErrNoExistOrNoAccess, ctx, l.Name())
}
return verror.New(verror.ErrInternal, ctx, err)
}
return nil
}
// Get does GetWithoutAuth followed by an auth check.
// Returns a VDL-compatible error.
func Get(ctx *context.T, call rpc.ServerCall, st store.StoreReader, l Layer, v Permser) error {
if err := GetWithoutAuth(ctx, call, st, l, v); err != nil {
return err
}
auth, _ := access.PermissionsAuthorizer(v.GetPerms(), access.TypicalTagType())
if err := auth.Authorize(ctx, call.Security()); err != nil {
// TODO(sadovsky): Return ErrNoAccess if appropriate.
return verror.New(verror.ErrNoExistOrNoAccess, ctx, l.Name())
}
return nil
}
// Put does st.Put(l.StKey(), v).
// Returns a VDL-compatible error.
// If you need to perform an authorization check, use Update().
func Put(ctx *context.T, _ rpc.ServerCall, st store.StoreWriter, l Layer, v interface{}) error {
if err := PutObject(st, l.StKey(), v); err != nil {
return verror.New(verror.ErrInternal, ctx, err)
}
return nil
}
// Delete does st.Delete(l.StKey()).
// Returns a VDL-compatible error.
// If you need to perform an authorization check, call Get() first.
func Delete(ctx *context.T, _ rpc.ServerCall, st store.StoreWriter, l Layer) error {
if err := st.Delete([]byte(l.StKey())); err != nil {
return verror.New(verror.ErrInternal, ctx, err)
}
return nil
}
// Update performs a read-modify-write.
// Input v is populated by the "read" step. fn should "modify" v, and should
// return a VDL-compatible error.
// Performs an auth check as part of the "read" step.
// Returns a VDL-compatible error.
func Update(ctx *context.T, call rpc.ServerCall, st store.StoreReadWriter, l Layer, v Permser, fn func() error) error {
_ = st.(store.Transaction) // panics on failure, as desired
if err := Get(ctx, call, st, l, v); err != nil {
return err
}
if err := fn(); err != nil {
return err
}
return Put(ctx, call, st, l, v)
}
////////////////////////////////////////////////////////////
// RPC-oblivious, lower-level get/put
func GetObject(st store.StoreReader, k string, v interface{}) error {
bytes, err := st.Get([]byte(k), nil)
if err != nil {
return err
}
return vom.Decode(bytes, v)
}
func PutObject(st store.StoreWriter, k string, v interface{}) error {
bytes, err := vom.Encode(v)
if err != nil {
return err
}
return st.Put([]byte(k), bytes)
}