blob: a9f3b2b000f2e278911417bd1c0ef1962250277e [file] [log] [blame]
package state_test
import (
"runtime"
"testing"
"veyron/services/store/memstore/refs"
"veyron/services/store/memstore/state"
"veyron2/security"
"veyron2/storage"
)
// check that the iterator produces a set of names.
func checkAcyclicIterator(t *testing.T, sn *state.MutableSnapshot, id security.PublicID, filter state.IterFilter, names []string) {
_, file, line, _ := runtime.Caller(1)
// Construct an index of names.
index := map[string]bool{}
for _, name := range names {
index[name] = false
}
// Compute the found names.
for it := sn.NewIterator(id, storage.ParsePath("/"), state.ListPaths, filter); it.IsValid(); it.Next() {
name := it.Name()
if found, ok := index[name]; ok {
if found {
t.Errorf("%s(%d): duplicate name %q", file, line, name)
}
index[name] = true
} else {
t.Errorf("%s(%d): unexpected name %q", file, line, name)
}
}
// Print the not found names.
for name, found := range index {
if !found {
t.Errorf("%s(%d): expected: %v", file, line, name)
}
}
}
// check that the iterator produces a set of names. Since entries in the store
// can have multiple names, the names are provided using a set of equivalence
// classes. The requirement is that the iterator produces exactly one name from
// each equivalence class. Order doesn't matter.
func checkUniqueObjectsIterator(t *testing.T, sn *state.MutableSnapshot, id security.PublicID, filter state.IterFilter, names [][]string) {
_, file, line, _ := runtime.Caller(1)
// Construct an index of name to equivalence class.
index := map[string]int{}
for i, equiv := range names {
for _, name := range equiv {
index[name] = i
}
}
// Compute the found set of equivalence classes.
found := map[int]bool{}
for it := sn.NewIterator(id, storage.ParsePath("/"), state.ListObjects, filter); it.IsValid(); it.Next() {
name := it.Name()
if i, ok := index[name]; ok {
if _, ok := found[i]; ok {
t.Errorf("%s(%d): duplicate name %q", file, line, name)
}
found[i] = true
} else {
t.Errorf("%s(%d): unexpected name %q", file, line, name)
}
}
// Print the not found equivalence classes.
for i, equiv := range names {
if !found[i] {
t.Errorf("%s(%d): expected one of: %v", file, line, equiv)
}
}
}
// Tests that an iterator returns all non-cyclic paths that reach an object.
func TestDuplicatePaths(t *testing.T) {
st := state.New(rootPublicID)
sn := st.MutableSnapshot()
// Add some objects
put(t, sn, rootPublicID, "/", "")
put(t, sn, rootPublicID, "/teams", "")
put(t, sn, rootPublicID, "/teams/cardinals", "")
put(t, sn, rootPublicID, "/players", "")
mattID := put(t, sn, rootPublicID, "/players/matt", "")
// Add some hard links
put(t, sn, rootPublicID, "/teams/cardinals/mvp", mattID)
checkAcyclicIterator(t, sn, rootPublicID, nil, []string{
"",
"teams",
"players",
"teams/cardinals",
"players/matt",
"teams/cardinals/mvp",
})
checkUniqueObjectsIterator(t, sn, rootPublicID, nil, [][]string{
{""},
{"teams"},
{"players"},
{"teams/cardinals"},
{"players/matt", "teams/cardinals/mvp"},
})
// Test that the iterator does not revisit objects on previously rejected paths.
rejected := false
rejectMatt := func(fullPath *refs.FullPath, path *refs.Path) (bool, bool) {
name := fullPath.Append(path.Suffix(1)).Name().String()
if !rejected && (name == "players/matt" || name == "teams/cardinals/mvp") {
rejected = true
return false, true
}
return true, true
}
checkUniqueObjectsIterator(t, sn, rootPublicID, rejectMatt, [][]string{
{""},
{"teams"},
{"players"},
{"teams/cardinals"},
})
}
// Test that an iterator doesn't get stuck in cycles.
func TestCyclicStructure(t *testing.T) {
st := state.New(rootPublicID)
sn := st.MutableSnapshot()
// Add some objects
put(t, sn, rootPublicID, "/", "")
put(t, sn, rootPublicID, "/teams", "")
cardinalsID := put(t, sn, rootPublicID, "/teams/cardinals", "")
put(t, sn, rootPublicID, "/players", "")
mattID := put(t, sn, rootPublicID, "/players/matt", "")
put(t, sn, rootPublicID, "/players/joe", "")
// Add some hard links
put(t, sn, rootPublicID, "/players/matt/team", cardinalsID)
put(t, sn, rootPublicID, "/teams/cardinals/mvp", mattID)
checkAcyclicIterator(t, sn, rootPublicID, nil, []string{
"",
"teams",
"players",
"players/joe",
"players/matt",
"teams/cardinals/mvp",
"teams/cardinals",
"players/matt/team",
})
checkUniqueObjectsIterator(t, sn, rootPublicID, nil, [][]string{
{""},
{"teams"},
{"players"},
{"players/joe"},
{"players/matt", "teams/cardinals/mvp"},
{"teams/cardinals", "players/matt/team"},
})
}
func TestIteratorSecurity(t *testing.T) {
st := state.New(rootPublicID)
sn := st.MutableSnapshot()
// Create /Users/jane and give her RWA permissions.
janeACLID := putPath(t, sn, rootPublicID, "/Users/jane/acls/janeRWA", &storage.ACL{
Name: "Jane",
Contents: security.ACL{
janeUser: security.LabelSet(security.ReadLabel | security.WriteLabel | security.AdminLabel),
},
})
janeTags := storage.TagList{
storage.Tag{Op: storage.AddInheritedACL, ACL: janeACLID},
storage.Tag{Op: storage.RemoveACL, ACL: state.EveryoneACLID},
}
put(t, sn, rootPublicID, "/Users/jane/.tags", janeTags)
put(t, sn, rootPublicID, "/Users/jane/aaa", "stuff")
sharedID := put(t, sn, rootPublicID, "/Users/jane/shared", "stuff")
// Create /Users/john and give him RWA permissions.
johnACLID := putPath(t, sn, rootPublicID, "/Users/john/acls/johnRWA", &storage.ACL{
Name: "John",
Contents: security.ACL{
johnUser: security.LabelSet(security.ReadLabel | security.WriteLabel | security.AdminLabel),
},
})
johnTags := storage.TagList{
storage.Tag{Op: storage.AddInheritedACL, ACL: johnACLID},
storage.Tag{Op: storage.RemoveACL, ACL: state.EveryoneACLID},
}
put(t, sn, rootPublicID, "/Users/john/.tags", johnTags)
put(t, sn, rootPublicID, "/Users/john/aaa", "stuff")
put(t, sn, rootPublicID, "/Users/john/shared", sharedID)
// Root gets everything.
checkAcyclicIterator(t, sn, rootPublicID, nil, []string{
"",
"Users",
"Users/jane",
"Users/jane/acls",
"Users/jane/acls/janeRWA",
"Users/jane/aaa",
"Users/john",
"Users/john/acls",
"Users/john/acls/johnRWA",
"Users/john/aaa",
"Users/jane/shared",
"Users/john/shared",
})
checkUniqueObjectsIterator(t, sn, rootPublicID, nil, [][]string{
{""},
{"Users"},
{"Users/jane"},
{"Users/jane/acls"},
{"Users/jane/acls/janeRWA"},
{"Users/jane/aaa"},
{"Users/john"},
{"Users/john/acls"},
{"Users/john/acls/johnRWA"},
{"Users/john/aaa"},
{"Users/jane/shared", "Users/john/shared"},
})
// Jane sees only her names.
checkAcyclicIterator(t, sn, janePublicID, nil, []string{
"",
"Users",
"Users/jane",
"Users/jane/acls",
"Users/jane/acls/janeRWA",
"Users/jane/aaa",
"Users/jane/shared",
})
checkUniqueObjectsIterator(t, sn, janePublicID, nil, [][]string{
{""},
{"Users"},
{"Users/jane"},
{"Users/jane/acls"},
{"Users/jane/acls/janeRWA"},
{"Users/jane/aaa"},
{"Users/jane/shared"},
})
// John sees only his names.
checkAcyclicIterator(t, sn, johnPublicID, nil, []string{
"",
"Users",
"Users/john",
"Users/john/acls",
"Users/john/acls/johnRWA",
"Users/john/aaa",
"Users/john/shared",
})
checkUniqueObjectsIterator(t, sn, johnPublicID, nil, [][]string{
{""},
{"Users"},
{"Users/john"},
{"Users/john/acls"},
{"Users/john/acls/johnRWA"},
{"Users/john/aaa"},
{"Users/john/shared"},
})
}