Example #1
0
// 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,
	})
}
Example #2
0
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()
}