open-vault/vault/quotas/quotas_test.go

165 lines
6.7 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package quotas
import (
"context"
"testing"
"time"
"github.com/go-test/deep"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/metricsutil"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/stretchr/testify/require"
)
func TestQuotas_MountPathOverwrite(t *testing.T) {
qm, err := NewManager(logging.NewVaultLogger(log.Trace), nil, metricsutil.BlackholeSink(), true)
require.NoError(t, err)
quota := NewRateLimitQuota("tq", "", "kv1/", "", "", 10, time.Second, 0)
require.NoError(t, qm.SetQuota(context.Background(), TypeRateLimit.String(), quota, false))
quota = quota.Clone().(*RateLimitQuota)
quota.MountPath = "kv2/"
require.NoError(t, qm.SetQuota(context.Background(), TypeRateLimit.String(), quota, false))
q, err := qm.QueryQuota(&Request{
Type: TypeRateLimit,
MountPath: "kv1/",
})
require.NoError(t, err)
require.Nil(t, q)
require.NoError(t, qm.DeleteQuota(context.Background(), TypeRateLimit.String(), "tq"))
q, err = qm.QueryQuota(&Request{
Type: TypeRateLimit,
MountPath: "kv1/",
})
require.NoError(t, err)
require.Nil(t, q)
}
func TestQuotas_Precedence(t *testing.T) {
qm, err := NewManager(logging.NewVaultLogger(log.Trace), nil, metricsutil.BlackholeSink(), true)
require.NoError(t, err)
setQuotaFunc := func(t *testing.T, name, nsPath, mountPath, pathSuffix, role string) Quota {
t.Helper()
quota := NewRateLimitQuota(name, nsPath, mountPath, pathSuffix, role, 10, time.Second, 0)
require.NoError(t, qm.SetQuota(context.Background(), TypeRateLimit.String(), quota, true))
return quota
}
checkQuotaFunc := func(t *testing.T, nsPath, mountPath, pathSuffix, role string, expected Quota) {
t.Helper()
quota, err := qm.QueryQuota(&Request{
Type: TypeRateLimit,
NamespacePath: nsPath,
MountPath: mountPath,
Role: role,
Path: nsPath + mountPath + pathSuffix,
})
require.NoError(t, err)
if diff := deep.Equal(expected, quota); len(diff) > 0 {
t.Fatal(diff)
}
}
// No quota present. Expect nil.
checkQuotaFunc(t, "", "", "", "", nil)
// Define global quota and expect that to be returned.
rateLimitGlobalQuota := setQuotaFunc(t, "rateLimitGlobalQuota", "", "", "", "")
checkQuotaFunc(t, "", "", "", "", rateLimitGlobalQuota)
// Define a global mount specific quota and expect that to be returned.
rateLimitGlobalMountQuota := setQuotaFunc(t, "rateLimitGlobalMountQuota", "", "testmount/", "", "")
checkQuotaFunc(t, "", "testmount/", "", "", rateLimitGlobalMountQuota)
// Define a global mount + path specific quota and expect that to be returned.
rateLimitGlobalMountPathQuota := setQuotaFunc(t, "rateLimitGlobalMountPathQuota", "", "testmount/", "testpath", "")
checkQuotaFunc(t, "", "testmount/", "testpath", "", rateLimitGlobalMountPathQuota)
// Define a namespace quota and expect that to be returned.
rateLimitNSQuota := setQuotaFunc(t, "rateLimitNSQuota", "testns/", "", "", "")
checkQuotaFunc(t, "testns/", "", "", "", rateLimitNSQuota)
// Define a namespace mount specific quota and expect that to be returned.
rateLimitNSMountQuota := setQuotaFunc(t, "rateLimitNSMountQuota", "testns/", "testmount/", "", "")
checkQuotaFunc(t, "testns/", "testmount/", "testpath", "", rateLimitNSMountQuota)
// Define a namespace mount + glob and expect that to be returned.
rateLimitNSMountGlob := setQuotaFunc(t, "rateLimitNSMountGlob", "testns/", "testmount/", "*", "")
checkQuotaFunc(t, "testns/", "testmount/", "testpath", "", rateLimitNSMountGlob)
// Define a namespace mount + path specific quota with a glob and expect that to be returned.
rateLimitNSMountPathSuffixGlob := setQuotaFunc(t, "rateLimitNSMountPathSuffixGlob", "testns/", "testmount/", "test*", "")
checkQuotaFunc(t, "testns/", "testmount/", "testpath", "", rateLimitNSMountPathSuffixGlob)
// Define a namespace mount + path specific quota with a glob at the end of the path and expect that to be returned.
rateLimitNSMountPathSuffixGlobAfterPath := setQuotaFunc(t, "rateLimitNSMountPathSuffixGlobAfterPath", "testns/", "testmount/", "testpath*", "")
checkQuotaFunc(t, "testns/", "testmount/", "testpath", "", rateLimitNSMountPathSuffixGlobAfterPath)
// Define a namespace mount + path specific quota and expect that to be returned.
rateLimitNSMountPathQuota := setQuotaFunc(t, "rateLimitNSMountPathQuota", "testns/", "testmount/", "testpath", "")
checkQuotaFunc(t, "testns/", "testmount/", "testpath", "", rateLimitNSMountPathQuota)
// Define a namespace mount + role specific quota and expect that to be returned.
rateLimitNSMountRoleQuota := setQuotaFunc(t, "rateLimitNSMountPathQuota", "testns/", "testmount/", "", "role")
checkQuotaFunc(t, "testns/", "testmount/", "", "role", rateLimitNSMountRoleQuota)
// Now that many quota types are defined, verify that the most specific
// matches are returned per namespace.
checkQuotaFunc(t, "", "", "", "", rateLimitGlobalQuota)
checkQuotaFunc(t, "testns/", "", "", "", rateLimitNSQuota)
}
// TestQuotas_QueryRoleQuotas checks to see if quota creation on a mount
// requires a call to ResolveRoleOperation.
func TestQuotas_QueryResolveRole_RateLimitQuotas(t *testing.T) {
leaseWalkFunc := func(context.Context, func(request *Request) bool) error {
return nil
}
qm, err := NewManager(logging.NewVaultLogger(log.Trace), leaseWalkFunc, metricsutil.BlackholeSink(), true)
require.NoError(t, err)
rlqReq := &Request{
Type: TypeRateLimit,
Path: "",
MountPath: "mount1/",
NamespacePath: "",
ClientAddress: "127.0.0.1",
}
// Check that we have no quotas requiring role resolution on mount1/
required, err := qm.QueryResolveRoleQuotas(rlqReq)
require.NoError(t, err)
require.False(t, required)
// Create a non-role-based RLQ on mount1/ and make sure it doesn't require role resolution
rlq := NewRateLimitQuota("tq", rlqReq.NamespacePath, rlqReq.MountPath, rlqReq.Path, rlqReq.Role, 10, 1*time.Minute, 10*time.Second)
require.NoError(t, qm.SetQuota(context.Background(), TypeRateLimit.String(), rlq, false))
required, err = qm.QueryResolveRoleQuotas(rlqReq)
require.NoError(t, err)
require.False(t, required)
// Create a role-based RLQ on mount1/ and make sure it requires role resolution
rlqReq.Role = "test"
rlq = NewRateLimitQuota("tq", rlqReq.NamespacePath, rlqReq.MountPath, rlqReq.Path, rlqReq.Role, 10, 1*time.Minute, 10*time.Second)
require.NoError(t, qm.SetQuota(context.Background(), TypeRateLimit.String(), rlq, false))
required, err = qm.QueryResolveRoleQuotas(rlqReq)
require.NoError(t, err)
require.True(t, required)
// Check that we have no quotas requiring role resolution on mount2/
rlqReq.MountPath = "mount2/"
required, err = qm.QueryResolveRoleQuotas(rlqReq)
require.NoError(t, err)
require.False(t, required)
}