// AddRequest is called on every client request to update the // lastUpdateTS to prevent live transactions from being considered // abandoned and garbage collected. Read/write mutating requests have // their key(s) added to the transaction's keys slice for eventual // cleanup via resolved write intents. func (tc *coordinator) AddRequest(method string, header *proto.RequestHeader) { // Ignore non-transactional requests. if header.Txn == nil || !isTransactional(method) { return } tc.Lock() defer tc.Unlock() if _, ok := tc.txns[string(header.Txn.ID)]; !ok { tc.txns[string(header.Txn.ID)] = &txnMetadata{ txn: *header.Txn, lastUpdateTS: tc.clock.Now(), timeoutDuration: tc.clientTimeout, closer: make(chan struct{}), } // TODO(jiajia): Reevaluate this logic of creating a goroutine // for each active transaction. Spencer suggests a heap // containing next heartbeat timeouts which is processed by a // single goroutine. go tc.heartbeat(header.Txn, tc.txns[string(header.Txn.ID)].closer) } txnMeta := tc.txns[string(header.Txn.ID)] txnMeta.lastUpdateTS = tc.clock.Now() // If read-only, exit now; otherwise, store the affected key range. if storage.IsReadOnly(method) { return } // Otherwise, append a new key range to the set of affected keys. txnMeta.keys = append(txnMeta.keys, engine.KeyRange{ Start: header.Key, End: header.EndKey, }) }
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() }