blob: e076030d25e33857c6669f999d68179aa151bd6c [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/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.
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.DatabaseForId(wire.Id{Blessing: config.AppName, Name: config.DBName}, nil)
if err := createIfMissing(ctx, db, aclFull); err != nil {
return nil, err
}
// TODO(ivanpi): Add schemas when available.
for _, cs := range collections {
c := db.Collection(cs.Prototype.Collection())
acl := aclReadOnly
if !cs.ReadOnly {
acl = aclFull
}
if err := createIfMissing(ctx, c, acl); 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))
}
}