blob: 55ab4b5da7a93deb2f24c3bcd4eef28b4d9f0a7f [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.
// Utilities for creating and opening the database in Syncbase.
package sbutil
import (
"v.io/v23"
"v.io/v23/context"
"v.io/v23/security"
"v.io/v23/security/access"
wire "v.io/v23/services/syncbase"
"v.io/v23/syncbase"
"v.io/v23/syncbase/util"
"v.io/v23/verror"
"v.io/x/sensorlog/internal/config"
"v.io/x/sensorlog/internal/sbmodel"
)
// CreateOrOpenDB opens the Sensor Log database hosted on specified Syncbase
// instance, creating it if missing, and initializing specified collections.
// TODO(ivanpi): Collection ids currently use a hardwired user blessing. The
// blessing should instead be provided as a command-line argument, or taken
// from ctx if not provided.
func CreateOrOpenDB(ctx *context.T, sbService string, collections []sbmodel.CollectionSpec) (syncbase.Database, error) {
aclFull := access.Permissions{}
// Allow everyone to resolve to allow joining syncgroups.
AddPermsForPattern(&aclFull, string(security.AllPrincipals), access.Resolve)
// Restrict other permissions to self.
AddPermsForPrincipal(&aclFull, v23.GetPrincipal(ctx), access.Read, access.Write, access.Admin)
aclReadOnly := access.Permissions{}
// Allow everyone to resolve to allow joining syncgroups.
AddPermsForPattern(&aclReadOnly, string(security.AllPrincipals), access.Resolve)
// Restrict other permissions to self, except Write.
AddPermsForPrincipal(&aclReadOnly, v23.GetPrincipal(ctx), access.Read, access.Admin)
sbs := syncbase.NewService(sbService)
// TODO(sadovsky): Refactor to reflect the change that eliminates the explicit
// app layer. For now, we simply use AppName as database Id.Blessing.
// TODO(ivanpi): Add schema version.
db := sbs.Database(ctx, config.DBName, nil)
dbAcl := util.FilterTags(aclFull, wire.AllDatabaseTags...)
if err := createIfMissing(ctx, db, dbAcl); err != nil {
return nil, err
}
// TODO(ivanpi): Add schemas when available.
for _, cs := range collections {
c := db.Collection(ctx, cs.Prototype.Collection())
acl := aclReadOnly
if !cs.ReadOnly {
acl = aclFull
}
cxAcl := util.FilterTags(acl, wire.AllCollectionTags...)
if err := createIfMissing(ctx, c, cxAcl); err != nil {
return nil, err
}
}
return db, nil
}
// creatable is satisfied by Syncbase hierarchy layers (db, collection) that can
// be created and tested for existence.
type creatable interface {
Create(ctx *context.T, acl access.Permissions) error
Exists(ctx *context.T) (bool, error)
}
// createIfMissing checks if the given creatable layer exists and, if not,
// creates it.
// TODO(ivanpi): Syncbase client helpers for MustExist / CreateIfMissing.
func createIfMissing(ctx *context.T, target creatable, acl access.Permissions) error {
if exists, err := target.Exists(ctx); err != nil {
return err
} else if exists {
return nil
}
if err := target.Create(ctx, acl); err != nil && verror.ErrorID(err) != verror.ErrExist.ID {
return err
}
return nil
}
// AddPermsForPrincipal adds to the ACL all specified permissions for all
// default blessings of the provided principal.
func AddPermsForPrincipal(acl *access.Permissions, principal security.Principal, tags ...access.Tag) {
for _, pattern := range security.DefaultBlessingPatterns(principal) {
AddPermsForPattern(acl, string(pattern), tags...)
}
}
// AddPermsForPattern adds to the ACL all specified permissions for the
// specified pattern.
func AddPermsForPattern(acl *access.Permissions, pattern string, tags ...access.Tag) {
for _, tag := range tags {
acl.Add(security.BlessingPattern(pattern), string(tag))
}
}