// verifyPermissions verifies that the requesting user (header.User) // has permission to read/write (capabilities depend on method // name). In the event that multiple permission configs apply to the // key range implicated by the command, the lowest common denominator // for permission. For example, if a scan crosses two permission // configs, both configs must allow read permissions or the entire // scan will fail. func (db *DistDB) verifyPermissions(method string, header *storage.RequestHeader) error { // Get permissions map from gossip. permMap, err := db.gossip.GetInfo(gossip.KeyConfigPermission) if err != nil { return err } if permMap == nil { return util.Errorf("perm configs not available; cannot execute %s", method) } // Visit PermConfig(s) which apply to the method's key range. // - For each, verify each PermConfig allows reads or writes as method requires. end := header.EndKey if end == nil { end = header.Key } return permMap.(storage.PrefixConfigMap).VisitPrefixes( header.Key, end, func(start, end storage.Key, config interface{}) error { perm := config.(*storage.PermConfig) if storage.NeedReadPerm(method) && !perm.CanRead(header.User) { return util.Errorf("user %q cannot read range %q-%q; permissions: %+v", header.User, string(start), string(end), perm) } if storage.NeedWritePerm(method) && !perm.CanWrite(header.User) { return util.Errorf("user %q cannot read range %q-%q; permissions: %+v", header.User, string(start), string(end), perm) } return nil }) }
func TestVerifyPermissions(t *testing.T) { n := gossip.NewSimulationNetwork(1, "unix", gossip.DefaultTestGossipInterval) kv := NewDistKV(n.Nodes[0].Gossip) config1 := &proto.PermConfig{ Read: []string{"read1", "readAll", "rw", "rwAll"}, Write: []string{"write1", "writeAll", "rw", "rwAll"}} config2 := &proto.PermConfig{ Read: []string{"read2", "readAll", "rw2", "rwAll"}, Write: []string{"write2", "writeAll", "rw2", "rwAll"}} configs := []*storage.PrefixConfig{ {engine.KeyMin, nil, config1}, {engine.Key("a"), nil, config2}, } configMap, err := storage.NewPrefixConfigMap(configs) if err != nil { t.Fatalf("failed to make prefix config map, err: %s", err.Error()) } kv.gossip.AddInfo(gossip.KeyConfigPermission, configMap, time.Hour) readMethods := storage.ReadMethods writeMethods := storage.WriteMethods readOnlyMethods := make([]string, 0, len(readMethods)) writeOnlyMethods := make([]string, 0, len(writeMethods)) readWriteMethods := make([]string, 0, len(readMethods)+len(writeMethods)) for _, readM := range readMethods { if storage.IsReadOnly(readM) { readOnlyMethods = append(readOnlyMethods, readM) } else { readWriteMethods = append(readWriteMethods, readM) } } for _, writeM := range writeMethods { if !storage.NeedReadPerm(writeM) { writeOnlyMethods = append(writeOnlyMethods, writeM) } } testData := []struct { // Permission-based db methods from the storage package. methods []string user string startKey, endKey engine.Key hasPermission bool }{ // Test permissions within a single range {readOnlyMethods, "read1", engine.KeyMin, engine.KeyMin, true}, {readOnlyMethods, "rw", engine.KeyMin, engine.KeyMin, true}, {readOnlyMethods, "write1", engine.KeyMin, engine.KeyMin, false}, {readOnlyMethods, "random", engine.KeyMin, engine.KeyMin, false}, {readWriteMethods, "rw", engine.KeyMin, engine.KeyMin, true}, {readWriteMethods, "read1", engine.KeyMin, engine.KeyMin, false}, {readWriteMethods, "write1", engine.KeyMin, engine.KeyMin, false}, {writeOnlyMethods, "write1", engine.KeyMin, engine.KeyMin, true}, {writeOnlyMethods, "rw", engine.KeyMin, engine.KeyMin, true}, {writeOnlyMethods, "read1", engine.KeyMin, engine.KeyMin, false}, {writeOnlyMethods, "random", engine.KeyMin, engine.KeyMin, false}, // Test permissions across both ranges {readOnlyMethods, "readAll", engine.KeyMin, engine.Key("b"), true}, {readOnlyMethods, "read1", engine.KeyMin, engine.Key("b"), false}, {readOnlyMethods, "read2", engine.KeyMin, engine.Key("b"), false}, {readOnlyMethods, "random", engine.KeyMin, engine.Key("b"), false}, {readWriteMethods, "rwAll", engine.KeyMin, engine.Key("b"), true}, {readWriteMethods, "rw", engine.KeyMin, engine.Key("b"), false}, {readWriteMethods, "random", engine.KeyMin, engine.Key("b"), false}, {writeOnlyMethods, "writeAll", engine.KeyMin, engine.Key("b"), true}, {writeOnlyMethods, "write1", engine.KeyMin, engine.Key("b"), false}, {writeOnlyMethods, "write2", engine.KeyMin, engine.Key("b"), false}, {writeOnlyMethods, "random", engine.KeyMin, engine.Key("b"), false}, // Test permissions within and around the boundaries of a range, // representatively using rw methods. {readWriteMethods, "rw2", engine.Key("a"), engine.Key("b"), true}, {readWriteMethods, "rwAll", engine.Key("a"), engine.Key("b"), true}, {readWriteMethods, "rw2", engine.Key("a"), engine.Key("a"), true}, {readWriteMethods, "rw2", engine.Key("a"), engine.Key("a1"), true}, {readWriteMethods, "rw2", engine.Key("a"), engine.Key("b1"), false}, {readWriteMethods, "rw2", engine.Key("a3"), engine.Key("a4"), true}, {readWriteMethods, "rw2", engine.Key("a3"), engine.Key("b1"), false}, } for _, test := range testData { for _, method := range test.methods { err := kv.verifyPermissions( method, &proto.RequestHeader{ User: test.user, Key: test.startKey, EndKey: test.endKey}) if err != nil && test.hasPermission { t.Errorf("user: %s should have had permission to %s, err: %s", test.user, method, err.Error()) } else if err == nil && !test.hasPermission { t.Errorf("user: %s should not have had permission to %s", test.user, method) } } } n.Stop() }