re-enable the permission check for SetSyngroupSpec

The check that the caller of SetSyncgroupSpec has the
Admin ACL was removed since only syncbase servers had
the Admin permission. This CL re-enables that check
and changes the featuretests syncbase server blessings
to be client-based.

This CL makes it so that the clients all have blessings
of root:o:app:client:c%d and the syncgroup acls are set
to name specific clients. The servers are all configured to
have the blessings of all the clients, e.g.

root:o:app:client:c0,root:o:app:client:c1,root:o:app:client:c2,root:o:app:client:c3,root:o:app:client:c4

MultiPart: 1/2
Change-Id: I3bd87298a5a2a526e3c3028abb0c7f02fc0c7083
diff --git a/syncbase/featuretests/blob_v23_test.go b/syncbase/featuretests/blob_v23_test.go
index 258305f..6c0a813 100644
--- a/syncbase/featuretests/blob_v23_test.go
+++ b/syncbase/featuretests/blob_v23_test.go
@@ -30,7 +30,7 @@
 
 	ok(t, createCollection(sbs[0].clientCtx, sbs[0].sbName, testCx.Name))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, testCx.Name, "foo", 0, 10))
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "", nil, clBlessings(sbs)))
 	ok(t, joinSyncgroup(sbs[1].clientCtx, sbs[1].sbName, sbs[0].sbName, sgId))
 	ok(t, verifySyncgroupData(sbs[1].clientCtx, sbs[1].sbName, testCx.Name, "foo", "", 0, 10))
 
diff --git a/syncbase/featuretests/cr_v23_test.go b/syncbase/featuretests/cr_v23_test.go
index 52ffcde..cd5f29e 100644
--- a/syncbase/featuretests/cr_v23_test.go
+++ b/syncbase/featuretests/cr_v23_test.go
@@ -325,7 +325,7 @@
 	sgId = wire.Id{Name: "SG1", Blessing: testCx.Blessing}
 
 	// Create syncgroup and populate data on s0.
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c", "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c", "", nil, clBlessings(sbs)))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, testCx.Name, "foo", 0, numInitRows))
 
 	// Join syncgroup and verify data on s1.
diff --git a/syncbase/featuretests/ping_pong_test.go b/syncbase/featuretests/ping_pong_test.go
index a7d943f..038a2ad 100644
--- a/syncbase/featuretests/ping_pong_test.go
+++ b/syncbase/featuretests/ping_pong_test.go
@@ -60,7 +60,7 @@
 			// Syncbase s0 is the creator.
 			sgId := wire.Id{Name: fmt.Sprintf("SG%d", g+1), Blessing: testCx.Blessing}
 
-			ok(b, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "", sbBlessings(sbs), nil, clBlessings(sbs)))
+			ok(b, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "", nil, clBlessings(sbs)))
 
 			// The other syncbases will attempt to join the syncgroup.
 			for i := 1; i < *numSync; i++ {
diff --git a/syncbase/featuretests/sync_v23_test.go b/syncbase/featuretests/sync_v23_test.go
index c5b59dc..3d99854 100644
--- a/syncbase/featuretests/sync_v23_test.go
+++ b/syncbase/featuretests/sync_v23_test.go
@@ -57,7 +57,7 @@
 	sbName := sbs[0].sbName
 	sgId := wire.Id{Name: "SG1", Blessing: testCx.Blessing}
 
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c", "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c", "", nil, clBlessings(sbs)))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, testCx.Name, "foo", 0, 10))
 
 	// This is a decoy syncgroup that no other Syncbase joins, but is on the
@@ -69,7 +69,7 @@
 	sgId1 := wire.Id{Name: "SG2", Blessing: testCx.Blessing}
 
 	// Verify data syncing (client0 updates).
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId1, "c1", "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId1, "c1", "", nil, clBlessings(sbs)))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, "c1", "foo", 0, 10))
 
 	ok(t, joinSyncgroup(sbs[1].clientCtx, sbs[1].sbName, sbName, sgId))
@@ -87,20 +87,20 @@
 	sbs[1].cleanup = sh.StartSyncbase(sbs[1].sbCreds, syncbaselib.Opts{Name: sbs[1].sbName, RootDir: sbs[1].rootDir}, acl(sbs[1].clientId))
 
 	// Verify syncgroup syncing (client0 updates).
-	ok(t, setSyncgroupSpec(sbs[0].clientCtx, sbs[0].sbName, sgId, "v2", "c", "", "root:s0;root:s1;root:s3", nil))
-	ok(t, verifySyncgroupSpec(sbs[1].clientCtx, sbs[1].sbName, sgId, "v2", "c", "root:s0;root:s1;root:s3"))
+	ok(t, setSyncgroupSpec(sbs[0].clientCtx, sbs[0].sbName, sgId, "v2", "c", "", "root:o:app:client:c0;root:o:app:client:c1;root:o:app:client:c3", nil))
+	ok(t, verifySyncgroupSpec(sbs[1].clientCtx, sbs[1].sbName, sgId, "v2", "c", "root:o:app:client:c0;root:o:app:client:c1;root:o:app:client:c3"))
 
 	// Verify data and syncgroup syncing (client1 updates).
 	ok(t, updateData(sbs[1].clientCtx, sbs[1].sbName, 5, 10, ""))
 	ok(t, populateData(sbs[1].clientCtx, sbs[1].sbName, testCx.Name, "foo", 10, 20))
-	ok(t, setSyncgroupSpec(sbs[1].clientCtx, sbs[1].sbName, sgId, "v3", "c", "", "root:s0;root:s1;root:s4", nil))
+	ok(t, setSyncgroupSpec(sbs[1].clientCtx, sbs[1].sbName, sgId, "v3", "c", "", "root:o:app:client:c0;root:o:app:client:c1;root:o:app:client:c4", nil))
 
 	// Verify that the last updates are synced right away so that we are
 	// assured that all the updates are synced.
 	ok(t, verifySyncgroupData(sbs[0].clientCtx, sbs[0].sbName, testCx.Name, "foo", "", 10, 10))
 	ok(t, verifySyncgroupData(sbs[0].clientCtx, sbs[0].sbName, testCx.Name, "foo", "testkey"+sbs[1].sbName, 5, 5))
 	ok(t, verifySyncgroupData(sbs[0].clientCtx, sbs[0].sbName, testCx.Name, "foo", "", 0, 5))
-	ok(t, verifySyncgroupSpec(sbs[0].clientCtx, sbs[0].sbName, sgId, "v3", "c", "root:s0;root:s1;root:s4"))
+	ok(t, verifySyncgroupSpec(sbs[0].clientCtx, sbs[0].sbName, sgId, "v3", "c", "root:o:app:client:c0;root:o:app:client:c1;root:o:app:client:c4"))
 
 	// Shutdown and restart Syncbase instances.
 	sbs[0].cleanup(os.Interrupt)
@@ -109,8 +109,8 @@
 	sbs[0].cleanup = sh.StartSyncbase(sbs[0].sbCreds, syncbaselib.Opts{Name: sbs[0].sbName, RootDir: sbs[0].rootDir}, acl(sbs[0].clientId))
 	sbs[1].cleanup = sh.StartSyncbase(sbs[1].sbCreds, syncbaselib.Opts{Name: sbs[1].sbName, RootDir: sbs[1].rootDir}, acl(sbs[1].clientId))
 
-	ok(t, verifySyncgroupSpec(sbs[0].clientCtx, sbs[0].sbName, sgId, "v3", "c", "root:s0;root:s1;root:s4"))
-	ok(t, verifySyncgroupSpec(sbs[1].clientCtx, sbs[1].sbName, sgId, "v3", "c", "root:s0;root:s1;root:s4"))
+	ok(t, verifySyncgroupSpec(sbs[0].clientCtx, sbs[0].sbName, sgId, "v3", "c", "root:o:app:client:c0;root:o:app:client:c1;root:o:app:client:c4"))
+	ok(t, verifySyncgroupSpec(sbs[1].clientCtx, sbs[1].sbName, sgId, "v3", "c", "root:o:app:client:c0;root:o:app:client:c1;root:o:app:client:c4"))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, testCx.Name, "foo", 20, 30))
 	ok(t, verifySyncgroupData(sbs[1].clientCtx, sbs[1].sbName, testCx.Name, "foo", "", 10, 20))
 	ok(t, verifySyncgroupData(sbs[1].clientCtx, sbs[1].sbName, testCx.Name, "foo", "testkey"+sbs[1].sbName, 5, 5))
@@ -137,7 +137,7 @@
 	sbName := sbs[0].sbName
 	sgId := wire.Id{Name: "SG1", Blessing: testCx.Blessing}
 
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c1,c2", "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c1,c2", "", nil, clBlessings(sbs)))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, "c1", "foo", 0, 10))
 
 	beforeSyncMarker, err := getResumeMarker(sbs[1].clientCtx, sbs[1].sbName)
@@ -179,7 +179,7 @@
 	sbName := sbs[0].sbName
 	sgId := wire.Id{Name: "SG1", Blessing: testCx.Blessing}
 
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c", "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c", "", nil, clBlessings(sbs)))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, "c", "foo", 0, 10))
 	ok(t, joinSyncgroup(sbs[1].clientCtx, sbs[1].sbName, sbName, sgId))
 	ok(t, verifySyncgroupData(sbs[1].clientCtx, sbs[1].sbName, "c", "foo", "", 0, 10))
@@ -216,7 +216,7 @@
 	// Pre-populate the data before creating the syncgroup.
 	ok(t, createCollection(sbs[0].clientCtx, sbs[0].sbName, "c"))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, "c", "f", 0, 10))
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sg1Id, "c", "", "root:s0;root:s1", nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sg1Id, "c", "", nil, clBlessings(sbs)))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, "c", "foo", 0, 10))
 
 	ok(t, joinSyncgroup(sbs[1].clientCtx, sbs[1].sbName, sb1Name, sg1Id))
@@ -225,7 +225,7 @@
 
 	// We are setting the collection ACL again at syncbase2 but it is not
 	// required.
-	ok(t, createSyncgroup(sbs[1].clientCtx, sbs[1].sbName, sg2Id, "c", "", "root:s1;root:s2", nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[1].clientCtx, sbs[1].sbName, sg2Id, "c", "", nil, clBlessings(sbs)))
 
 	ok(t, joinSyncgroup(sbs[2].clientCtx, sbs[2].sbName, sb2Name, sg2Id))
 	ok(t, verifySyncgroupData(sbs[2].clientCtx, sbs[2].sbName, "c", "foo", "", 0, 10))
@@ -248,7 +248,7 @@
 	sbName := sbs[0].sbName
 	sgId := wire.Id{Name: "SG1", Blessing: testCx.Blessing}
 
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c", "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, "c", "", nil, clBlessings(sbs)))
 	ok(t, populateData(sbs[0].clientCtx, sbs[0].sbName, "c", "foo", 0, 10))
 	ok(t, verifySyncgroupMembers(sbs[0].clientCtx, sbs[0].sbName, sgId, 1))
 
@@ -284,7 +284,7 @@
 	ok(t, setupAppMulti(sbs[0].clientCtx, sbs[0].sbName, na, nd))
 	ok(t, setupAppMulti(sbs[1].clientCtx, sbs[1].sbName, na, nd))
 
-	ok(t, populateAndCreateSyncgroupMulti(sbs[0].clientCtx, sbs[0].sbName, na, nd, nc, "foo,bar", sbBlessings(sbs), clBlessings(sbs)))
+	ok(t, populateAndCreateSyncgroupMulti(sbs[0].clientCtx, sbs[0].sbName, na, nd, nc, "foo,bar", clBlessings(sbs)))
 	ok(t, joinSyncgroupMulti(sbs[1].clientCtx, sbs[1].sbName, sbs[0].sbName, na, nd))
 	ok(t, verifySyncgroupDataMulti(sbs[1].clientCtx, sbs[1].sbName, na, nd, nc, "foo,bar"))
 }
@@ -366,15 +366,15 @@
 	return nil
 }
 
-func populateAndCreateSyncgroupMulti(ctx *context.T, syncbaseName string, numApps, numDbs, numCxs int, prefixStr, sbBlessings, clBlessings string) error {
+func populateAndCreateSyncgroupMulti(ctx *context.T, syncbaseName string, numApps, numDbs, numCxs int, prefixStr, blessings string) error {
 	roots := v23.GetNamespace(ctx).Roots()
 	if len(roots) == 0 {
 		return errors.New("no namespace roots")
 	}
 	mtName := roots[0]
 
-	sgperms := tu.DefaultPerms(wire.AllSyncgroupTags, strings.Split(sbBlessings, ";")...)
-	clperms := tu.DefaultPerms(wire.AllCollectionTags, strings.Split(clBlessings, ";")...)
+	sgperms := tu.DefaultPerms(wire.AllSyncgroupTags, strings.Split(blessings, ";")...)
+	clperms := tu.DefaultPerms(wire.AllCollectionTags, strings.Split(blessings, ";")...)
 
 	svc := syncbase.NewService(syncbaseName)
 
@@ -477,7 +477,6 @@
 	var spec wire.SyncgroupSpec
 	var err error
 	for i := 0; i < 20; i++ {
-		time.Sleep(500 * time.Millisecond)
 		spec, _, err = sg.GetSpec(ctx)
 		if err != nil {
 			return fmt.Errorf("GetSpec SG %q failed: %v\n", sgId, err)
@@ -485,6 +484,7 @@
 		if spec.Description == wantDesc {
 			break
 		}
+		time.Sleep(500 * time.Millisecond)
 	}
 	if spec.Description != wantDesc || !reflect.DeepEqual(spec.Collections, wantCollections) || !reflect.DeepEqual(spec.Perms, wantPerms) {
 		return fmt.Errorf("GetSpec SG %q failed: description got %v, want %v, collections got %v, want %v, perms got %v, want %v\n",
@@ -501,12 +501,12 @@
 	// Wait for a bit for deletions to propagate.
 	lastKey := fmt.Sprintf("%s%d", keyPrefix, start-1)
 	for i := 0; i < 20; i++ {
-		time.Sleep(500 * time.Millisecond)
 		r := c.Row(lastKey)
 		var value string
 		if err := r.Get(ctx, &value); verror.ErrorID(err) == verror.ErrNoExist.ID {
 			break
 		}
+		time.Sleep(500 * time.Millisecond)
 	}
 
 	if valuePrefix == "" {
@@ -614,11 +614,11 @@
 	lastKey := fmt.Sprintf("%s%d", keyPrefix, start+count-1)
 	r := c.Row(lastKey)
 	for i := 0; i < 20; i++ {
-		time.Sleep(500 * time.Millisecond)
 		var value string
 		if err := r.Get(ctx, &value); verror.ErrorID(err) == verror.ErrNoAccess.ID {
 			break
 		}
+		time.Sleep(500 * time.Millisecond)
 	}
 
 	// Verify that all keys have lost access.
@@ -656,10 +656,10 @@
 						var err error
 						// Wait for some time to sync.
 						for t := 0; t < 20; t++ {
-							time.Sleep(500 * time.Millisecond)
 							if err = r.Get(ctx, &got); err == nil {
 								break
 							}
+							time.Sleep(500 * time.Millisecond)
 						}
 						if err != nil {
 							return fmt.Errorf("r.Get() failed: %v\n", err)
diff --git a/syncbase/featuretests/syncgroup_v23_test.go b/syncbase/featuretests/syncgroup_v23_test.go
index d92b6bf..fc23c81 100644
--- a/syncbase/featuretests/syncgroup_v23_test.go
+++ b/syncbase/featuretests/syncgroup_v23_test.go
@@ -9,13 +9,10 @@
 
 import (
 	"fmt"
-	"strings"
 	"testing"
 	"time"
 
 	"v.io/v23/context"
-	"v.io/v23/security"
-	"v.io/v23/security/access"
 	wire "v.io/v23/services/syncbase"
 	"v.io/x/ref/test/v23test"
 )
@@ -37,7 +34,7 @@
 	sbName := sbs[0].sbName
 	sgId := wire.Id{Name: "SG1", Blessing: testCx.Blessing}
 
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "", nil, clBlessings(sbs)))
 
 	// Remaining syncbases run the specified workload concurrently.
 	for i := 1; i < len(sbs); i++ {
@@ -80,7 +77,7 @@
 	sbName := sbs[N].sbName
 	sgId := wire.Id{Name: "SG1", Blessing: testCx.Blessing}
 
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "", sbBlessings(sbs), nil, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "", nil, clBlessings(sbs)))
 
 	// Remaining N-1 syncbases run the specified workload concurrently.
 	for i := 1; i < N; i++ {
@@ -129,14 +126,7 @@
 	// multiple admins on the neighborhood.
 	//
 	// TODO(hpucha): Change it to multi-admin scenario.
-	principals := sbBlessings(sbs)
-	perms := access.Permissions{}
-	for _, pattern := range strings.Split(principals, ";") {
-		perms.Add(security.BlessingPattern(pattern), string(access.Read))
-	}
-	perms.Add(security.BlessingPattern("root:"+sbs[0].sbName), string(access.Admin))
-
-	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "/mttable", "", perms, clBlessings(sbs)))
+	ok(t, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgId, testCx.Name, "/mttable", nil, clBlessings(sbs)))
 
 	// Remaining syncbases run the specified workload concurrently.
 	for i := 1; i < len(sbs); i++ {
@@ -176,12 +166,12 @@
 	// stagger the process.
 	sbName := sbs[0].sbName
 	sgId := wire.Id{Name: "SG1", Blessing: testCx.Blessing}
-	ok(t, joinOrCreateSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sbName, sgId, testCx.Name, "", sbBlessings(sbs), clBlessings(sbs)))
+	ok(t, joinOrCreateSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sbName, sgId, testCx.Name, "", clBlessings(sbs)))
 
 	// Remaining syncbases join the syncgroup concurrently.
 	for i := 1; i < len(sbs); i++ {
 		go func(i int) {
-			ok(t, joinOrCreateSyncgroup(sbs[i].clientCtx, sbs[i].sbName, sbName, sgId, testCx.Name, "", sbBlessings(sbs), clBlessings(sbs)))
+			ok(t, joinOrCreateSyncgroup(sbs[i].clientCtx, sbs[i].sbName, sbName, sgId, testCx.Name, "", clBlessings(sbs)))
 		}(i)
 	}
 
@@ -244,9 +234,9 @@
 	return nil
 }
 
-func joinOrCreateSyncgroup(ctx *context.T, sbNameLocal, sbNameRemote string, sgId wire.Id, sgColls, mtName, bps, clbps string) error {
+func joinOrCreateSyncgroup(ctx *context.T, sbNameLocal, sbNameRemote string, sgId wire.Id, sgColls, mtName, clbps string) error {
 	if err := joinSyncgroup(ctx, sbNameLocal, sbNameRemote, sgId); err == nil {
 		return nil
 	}
-	return createSyncgroup(ctx, sbNameLocal, sgId, sgColls, mtName, bps, nil, clbps)
+	return createSyncgroup(ctx, sbNameLocal, sgId, sgColls, mtName, nil, clbps)
 }
diff --git a/syncbase/featuretests/test_util_test.go b/syncbase/featuretests/test_util_test.go
index d13c1ab..948a978 100644
--- a/syncbase/featuretests/test_util_test.go
+++ b/syncbase/featuretests/test_util_test.go
@@ -28,16 +28,18 @@
 )
 
 var (
-	testDb = wire.Id{Blessing: "root:o:app", Name: "d"}
-	testCx = wire.Id{Blessing: "root:o:app:client", Name: "c"}
+	appExtension    = "o:app"
+	clientExtension = appExtension + ":client"
+	testDb          = wire.Id{Blessing: "root:" + appExtension, Name: "d"}
+	testCx          = wire.Id{Blessing: "root:" + clientExtension, Name: "c"}
 )
 
 ////////////////////////////////////////////////////////////
 // Helpers for setting up Syncbases, dbs, and collections
 
-func setupHierarchy(ctx *context.T, syncbaseName string) error {
+func setupHierarchy(ctx *context.T, syncbaseName string, perms access.Permissions) error {
 	d := syncbase.NewService(syncbaseName).DatabaseForId(testDb, nil)
-	return d.Create(ctx, nil)
+	return d.Create(ctx, perms)
 }
 
 func createCollection(ctx *context.T, syncbaseName, collectionName string) error {
@@ -58,35 +60,33 @@
 // Spawns "num" Syncbase instances and returns handles to them.
 func setupSyncbases(t testing.TB, sh *v23test.Shell, num int, devMode bool) []*testSyncbase {
 	sbs := make([]*testSyncbase, num)
+
 	for i, _ := range sbs {
-		sbName, clientId := fmt.Sprintf("s%d", i), fmt.Sprintf("o:app:client:c%d", i)
+		sbName, clientId := fmt.Sprintf("s%d", i), fmt.Sprintf("%s:c%d", clientExtension, i)
+		clientCtx := sh.ForkContext(clientId)
+		sbCreds := sh.ForkCredentialsFromPrincipal(v23.GetPrincipal(clientCtx), sbName)
+		if sh.Err != nil {
+			tu.Fatal(t, sh.Err)
+		}
 		sbs[i] = &testSyncbase{
 			sbName:    sbName,
-			sbCreds:   sh.ForkCredentials(sbName),
+			sbCreds:   sbCreds,
 			rootDir:   sh.MakeTempDir(),
 			clientId:  clientId,
-			clientCtx: sh.ForkContext(clientId),
+			clientCtx: clientCtx,
 		}
 		// Give XRWA permissions to this Syncbase's client.
-		acl := fmt.Sprintf(`{"Resolve":{"In":["root:%s"]},"Read":{"In":["root:%s"]},"Write":{"In":["root:%s"]},"Admin":{"In":["root:%s"]}}`, clientId, clientId, clientId, clientId)
+		clientBlessing := fmt.Sprintf("root:%s", clientId)
+		acl := fmt.Sprintf(`{"Resolve":{"In":["%s"]},"Read":{"In":["%s"]},"Write":{"In":["%s"]},"Admin":{"In":["%s"]}}`, clientBlessing, clientBlessing, clientBlessing, clientBlessing)
 		sbs[i].cleanup = sh.StartSyncbase(sbs[i].sbCreds, syncbaselib.Opts{Name: sbs[i].sbName, RootDir: sbs[i].rootDir, DevMode: devMode}, acl)
-	}
-	// Call setupHierarchy on each Syncbase.
-	for _, sb := range sbs {
-		ok(t, setupHierarchy(sb.clientCtx, sb.sbName))
+
+		// Call setupHierarchy on each Syncbase.
+		dbPerms := tu.DefaultPerms(wire.AllDatabaseTags, clientBlessing)
+		ok(t, setupHierarchy(sbs[i].clientCtx, sbs[i].sbName, dbPerms))
 	}
 	return sbs
 }
 
-// Returns a ";"-separated list of Syncbase blessing names.
-func sbBlessings(sbs []*testSyncbase) string {
-	names := make([]string, len(sbs))
-	for i, sb := range sbs {
-		names[i] = "root:" + sb.sbName
-	}
-	return strings.Join(names, ";")
-}
-
 // Returns a ";"-separated list of Syncbase clients blessing names.
 func clBlessings(sbs []*testSyncbase) string {
 	names := make([]string, len(sbs))
@@ -189,11 +189,11 @@
 	// Wait a bit (up to 10 seconds) for the last key to appear.
 	lastKey := fmt.Sprintf("%s%d", keyPrefix, start+count-1)
 	for i := 0; i < 20; i++ {
-		time.Sleep(500 * time.Millisecond)
 		var value string
 		if err := c.Get(ctx, lastKey, &value); err == nil {
 			break
 		}
+		time.Sleep(500 * time.Millisecond)
 	}
 
 	if valuePrefix == "" {
@@ -219,7 +219,7 @@
 // Helpers for managing syncgroups
 
 // blessingPatterns is a ";"-separated list of blessing patterns.
-func createSyncgroup(ctx *context.T, syncbaseName string, sgId wire.Id, sgColls, mtName, sbBlessings string, perms access.Permissions, clBlessings string) error {
+func createSyncgroup(ctx *context.T, syncbaseName string, sgId wire.Id, sgColls, mtName string, perms access.Permissions, clBlessings string) error {
 	if mtName == "" {
 		roots := v23.GetNamespace(ctx).Roots()
 		if len(roots) == 0 {
@@ -231,9 +231,9 @@
 	d := syncbase.NewService(syncbaseName).DatabaseForId(testDb, nil)
 
 	if perms == nil {
-		perms = tu.DefaultPerms(wire.AllSyncgroupTags, strings.Split(sbBlessings, ";")...)
+		perms = tu.DefaultPerms(wire.AllSyncgroupTags, strings.Split(clBlessings, ";")...)
 	}
-	clperms := tu.DefaultPerms(wire.AllCollectionTags, strings.Split(clBlessings, ";")...)
+	collectionPerms := tu.DefaultPerms(wire.AllCollectionTags, strings.Split(clBlessings, ";")...)
 
 	spec := wire.SyncgroupSpec{
 		Description: "test syncgroup sg",
@@ -250,7 +250,7 @@
 		c := d.CollectionForId(cId)
 		// Ignore the error since sometimes a collection might already exist.
 		c.Create(ctx, nil)
-		if err := c.SetPermissions(ctx, clperms); err != nil {
+		if err := c.SetPermissions(ctx, collectionPerms); err != nil {
 			return fmt.Errorf("{%q, %v} c.SetPermissions failed: %v", syncbaseName, cId, err)
 		}
 	}
@@ -280,7 +280,6 @@
 
 	var gotMembers int
 	for i := 0; i < 8; i++ {
-		time.Sleep(500 * time.Millisecond)
 		members, err := sg.GetMembers(ctx)
 		if err != nil {
 			return fmt.Errorf("{%q, %q} sg.GetMembers() failed: %v", syncbaseName, sgId, err)
@@ -289,6 +288,7 @@
 		if gotMembers == wantMembers {
 			break
 		}
+		time.Sleep(500 * time.Millisecond)
 	}
 	if gotMembers != wantMembers {
 		return fmt.Errorf("{%q, %q} verifySyncgroupMembers failed: got %d members, want %d", syncbaseName, sgId, gotMembers, wantMembers)
@@ -308,13 +308,12 @@
 // Syncbase-specific testing helpers
 
 // parseSgCollections converts, for example, "a,c" to
-// [Collection: {"root:o:app:client", "a"}, Collection: {"root:o:app:client", "c"}].
-// TODO(ivanpi): Change format to support user blessings other than "root:o:app:client".
+// [Collection: {clientBlessing, "a"}, Collection: {clientBlessing, "c"}].
 func parseSgCollections(csv string) []wire.Id {
 	strs := strings.Split(csv, ",")
 	res := make([]wire.Id, len(strs))
 	for i, v := range strs {
-		res[i] = wire.Id{"root:o:app:client", v}
+		res[i] = wire.Id{testCx.Blessing, v}
 	}
 	return res
 }
diff --git a/syncbase/featuretests/vclock_v23_test.go b/syncbase/featuretests/vclock_v23_test.go
index 19967a6..d3a9edf 100644
--- a/syncbase/featuretests/vclock_v23_test.go
+++ b/syncbase/featuretests/vclock_v23_test.go
@@ -400,7 +400,7 @@
 		sgId := wire.Id{Name: fmt.Sprintf("syncgroup%d", i), Blessing: "root"}
 		collectionName := testCx.Name + "_" + a.sbName + b.sbName
 		ok(t, createCollection(a.clientCtx, a.sbName, collectionName))
-		ok(t, createSyncgroup(a.clientCtx, a.sbName, sgId, collectionName, "", "root", nil, clBlessings(sbs)))
+		ok(t, createSyncgroup(a.clientCtx, a.sbName, sgId, collectionName, "", nil, clBlessings(sbs)))
 		ok(t, joinSyncgroup(b.clientCtx, b.sbName, a.sbName, sgId))
 
 		// Wait for a to see b.