forked from TrueCloudLab/frostfs-s3-gw
443 lines
13 KiB
Go
443 lines
13 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
const (
|
|
FrostfsVHSHeader = "X-Frostfs-S3-VHS"
|
|
FrostfsServernameHeader = "X-Frostfs-Servername"
|
|
)
|
|
|
|
type VHSSettingsMock struct {
|
|
domains []string
|
|
}
|
|
|
|
func (v *VHSSettingsMock) Domains() []string {
|
|
return v.domains
|
|
}
|
|
|
|
func (v *VHSSettingsMock) GlobalVHS() bool {
|
|
return false
|
|
}
|
|
|
|
func (v *VHSSettingsMock) VHSHeader() string {
|
|
return FrostfsVHSHeader
|
|
}
|
|
|
|
func (v *VHSSettingsMock) ServernameHeader() string {
|
|
return FrostfsServernameHeader
|
|
}
|
|
|
|
func (v *VHSSettingsMock) VHSNamespacesEnabled() map[string]bool {
|
|
return make(map[string]bool)
|
|
}
|
|
|
|
func TestIsVHSAddress(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
headerVHSEnabled string
|
|
vhsEnabledFlag bool
|
|
vhsNamespaced map[string]bool
|
|
namespace string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "vhs disabled",
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "vhs disabled for namespace",
|
|
vhsEnabledFlag: true,
|
|
vhsNamespaced: map[string]bool{
|
|
"kapusta": false,
|
|
},
|
|
namespace: "kapusta",
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "vhs enabled (global vhs flag)",
|
|
vhsEnabledFlag: true,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "vhs enabled for namespace",
|
|
vhsNamespaced: map[string]bool{
|
|
"kapusta": true,
|
|
},
|
|
namespace: "kapusta",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "vhs enabled (header)",
|
|
headerVHSEnabled: "true",
|
|
vhsEnabledFlag: false,
|
|
vhsNamespaced: map[string]bool{
|
|
"kapusta": false,
|
|
},
|
|
namespace: "kapusta",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "vhs disabled (header)",
|
|
headerVHSEnabled: "false",
|
|
vhsEnabledFlag: true,
|
|
vhsNamespaced: map[string]bool{
|
|
"kapusta": true,
|
|
},
|
|
namespace: "kapusta",
|
|
expected: false,
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
actual := isVHSAddress(tc.headerVHSEnabled, tc.vhsEnabledFlag, tc.vhsNamespaced, tc.namespace)
|
|
require.Equal(t, tc.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPreparePathStyleAddress(t *testing.T) {
|
|
bkt, obj := "test-bucket", "test-object"
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
urlParams string
|
|
expectedReqType ReqType
|
|
expectedBktName string
|
|
expectedObjName string
|
|
}{
|
|
{
|
|
name: "bucket request",
|
|
urlParams: "/" + bkt,
|
|
expectedReqType: bucketType,
|
|
expectedBktName: bkt,
|
|
},
|
|
{
|
|
name: "bucket request with slash",
|
|
urlParams: "/" + bkt + "/",
|
|
expectedReqType: bucketType,
|
|
expectedBktName: bkt,
|
|
},
|
|
{
|
|
name: "object request",
|
|
urlParams: "/" + bkt + "/" + obj,
|
|
expectedReqType: objectType,
|
|
expectedBktName: bkt,
|
|
expectedObjName: obj,
|
|
},
|
|
{
|
|
name: "object request with slash",
|
|
urlParams: "/" + bkt + "/" + obj + "/",
|
|
expectedReqType: objectType,
|
|
expectedBktName: bkt,
|
|
expectedObjName: obj + "/",
|
|
},
|
|
{
|
|
name: "none type request",
|
|
urlParams: "/",
|
|
expectedReqType: noneType,
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
reqInfo := &ReqInfo{}
|
|
r := httptest.NewRequest(http.MethodGet, tc.urlParams, nil)
|
|
|
|
preparePathStyleAddress(reqInfo, r, reqLogOrDefault(r.Context(), zaptest.NewLogger(t)))
|
|
require.Equal(t, tc.expectedReqType, reqInfo.RequestType)
|
|
require.Equal(t, tc.expectedBktName, reqInfo.BucketName)
|
|
require.Equal(t, tc.expectedObjName, reqInfo.ObjectName)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPrepareVHSAddress(t *testing.T) {
|
|
bkt, obj, domain := "test-bucket", "test-object", "domain.com"
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
domains []string
|
|
host string
|
|
urlParams string
|
|
expectedReqType ReqType
|
|
expectedBktName string
|
|
expectedObjName string
|
|
}{
|
|
{
|
|
name: "bucket request, the domain matched",
|
|
domains: []string{domain},
|
|
host: bkt + "." + domain,
|
|
urlParams: "/",
|
|
expectedReqType: bucketType,
|
|
expectedBktName: bkt,
|
|
},
|
|
{
|
|
name: "object request, the domain matched",
|
|
domains: []string{domain},
|
|
host: bkt + "." + domain,
|
|
urlParams: "/" + obj,
|
|
expectedReqType: objectType,
|
|
expectedBktName: bkt,
|
|
expectedObjName: obj,
|
|
},
|
|
{
|
|
name: "object request with slash, the domain matched",
|
|
domains: []string{domain},
|
|
host: bkt + "." + domain,
|
|
urlParams: "/" + obj + "/",
|
|
expectedReqType: objectType,
|
|
expectedBktName: bkt,
|
|
expectedObjName: obj + "/",
|
|
},
|
|
{
|
|
name: "list-buckets request, the domain matched",
|
|
domains: []string{domain},
|
|
host: domain,
|
|
urlParams: "/",
|
|
expectedReqType: noneType,
|
|
},
|
|
{
|
|
name: "bucket request, the domain don't match",
|
|
host: bkt + "." + domain,
|
|
urlParams: "/",
|
|
expectedReqType: bucketType,
|
|
expectedBktName: bkt,
|
|
},
|
|
{
|
|
name: "object request, the domain don't match",
|
|
host: bkt + "." + domain,
|
|
urlParams: "/" + obj,
|
|
expectedReqType: objectType,
|
|
expectedBktName: bkt,
|
|
expectedObjName: obj,
|
|
},
|
|
{
|
|
name: "object request with slash, the domain don't match",
|
|
host: bkt + "." + domain,
|
|
urlParams: "/" + obj + "/",
|
|
expectedReqType: objectType,
|
|
expectedBktName: bkt,
|
|
expectedObjName: obj + "/",
|
|
},
|
|
{
|
|
name: "list-buckets request, the domain don't match (list-buckets isn't supported if the domains don't match)",
|
|
host: domain,
|
|
urlParams: "/",
|
|
expectedReqType: bucketType,
|
|
expectedBktName: strings.Split(domain, ".")[0],
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
reqInfo := &ReqInfo{}
|
|
vhsSettings := &VHSSettingsMock{domains: tc.domains}
|
|
r := httptest.NewRequest(http.MethodGet, tc.urlParams, nil)
|
|
r.Host = tc.host
|
|
|
|
prepareVHSAddress(reqInfo, r, vhsSettings)
|
|
require.Equal(t, tc.expectedReqType, reqInfo.RequestType)
|
|
require.Equal(t, tc.expectedBktName, reqInfo.BucketName)
|
|
require.Equal(t, tc.expectedObjName, reqInfo.ObjectName)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckDomains(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
domains []string
|
|
requestURL string
|
|
expectedBktName string
|
|
expectedMatch bool
|
|
}{
|
|
{
|
|
name: "valid url with bktName and namespace (wildcard after protocol infix)",
|
|
domains: []string{"s3.<wildcard>.domain.com"},
|
|
requestURL: "bktA.s3.kapusta.domain.com",
|
|
expectedBktName: "bktA",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "valid url without bktName and namespace (wildcard after protocol infix)",
|
|
domains: []string{"s3.<wildcard>.domain.com"},
|
|
requestURL: "s3.kapusta.domain.com",
|
|
expectedBktName: "",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "invalid url with invalid bktName (wildcard after protocol infix)",
|
|
domains: []string{"s3.<wildcard>.domain.com"},
|
|
requestURL: "bktA.bktB.s3.kapusta.domain.com",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "invalid url without namespace (wildcard after protocol infix)",
|
|
domains: []string{"s3.<wildcard>.domain.com"},
|
|
requestURL: "bktA.s3.domain.com",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "invalid url with invalid infix (wildcard after protocol infix)",
|
|
domains: []string{"s3.<wildcard>.domain.com"},
|
|
requestURL: "bktA.s4.kapusta.domain.com",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "invalid url with invalid postfix (wildcard after protocol infix)",
|
|
domains: []string{"s3.<wildcard>.domain.com"},
|
|
requestURL: "bktA.s3.kapusta.dom.su",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "valid url with bktName and namespace (wildcard at the beginning of the domain)",
|
|
domains: []string{"<wildcard>.domain.com"},
|
|
requestURL: "bktA.kapusta.domain.com",
|
|
expectedBktName: "bktA",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "valid url without bktName and namespace (wildcard at the beginning of the domain)",
|
|
domains: []string{"<wildcard>.domain.com"},
|
|
requestURL: "kapusta.domain.com",
|
|
expectedBktName: "",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "invalid url with invalid bktName (wildcard at the beginning of the domain)",
|
|
domains: []string{"<wildcard>.domain.com"},
|
|
requestURL: "bktA.bktB.kapusta.domain.com",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "collision test - true, because we cannot clearly distinguish a namespace from a bucket (wildcard at the beginning of the domain)",
|
|
domains: []string{"<wildcard>.domain.com"},
|
|
requestURL: "bktA.domain.com",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "invalid url (fewer hosts)",
|
|
domains: []string{"<wildcard>.domain.com"},
|
|
requestURL: "domain.com",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "invalid url with invalid postfix (wildcard at the beginning of the domain)",
|
|
domains: []string{"<wildcard>.domain.com"},
|
|
requestURL: "bktA.kapusta.dom.su",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "valid url with bktName and without wildcard (root namaspace)",
|
|
domains: []string{"domain.com"},
|
|
requestURL: "bktA.domain.com",
|
|
expectedBktName: "bktA",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "valid url without bktName and without wildcard (root namaspace)",
|
|
domains: []string{"domain.com"},
|
|
requestURL: "domain.com",
|
|
expectedBktName: "",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "invalid url with bktName without wildcard (root namaspace)",
|
|
domains: []string{"domain.com"},
|
|
requestURL: "bktA.dom.su",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "invalid url without wildcard (root namaspace)",
|
|
domains: []string{"domain.com"},
|
|
requestURL: "dom.su",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "valid url, with a sorted list of domains",
|
|
domains: []string{"s3.<wildcard>.domain.com", "<wildcard>.domain.com", "domain.com"},
|
|
requestURL: "s3.kapusta.domain.com",
|
|
expectedBktName: "",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "valid url with bktName, multiple wildcards (wildcards at the beginning of the domain)",
|
|
domains: []string{"<wildcard>.<wildcard>.domain.com"},
|
|
requestURL: "bktA.s3.kapusta.domain.com",
|
|
expectedBktName: "bktA",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "valid url without bktName, multiple wildcards (wildcards at the beginning of the domain)",
|
|
domains: []string{"<wildcard>.<wildcard>.domain.com"},
|
|
requestURL: "s3.kapusta.domain.com",
|
|
expectedBktName: "",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "valid url with bktName, multiply wildcards",
|
|
domains: []string{"s3.<wildcard>.subdomain.<wildcard>.com"},
|
|
requestURL: "bktA.s3.kapusta.subdomain.domain.com",
|
|
expectedBktName: "bktA",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "valid url without bktName, multiply wildcards",
|
|
domains: []string{"s3.<wildcard>.subdomain.<wildcard>.com"},
|
|
requestURL: "s3.kapusta.subdomain.domain.com",
|
|
expectedBktName: "",
|
|
expectedMatch: true,
|
|
},
|
|
{
|
|
name: "invalid url without one wildcard",
|
|
domains: []string{"<wildcard>.<wildcard>.domain.com"},
|
|
requestURL: "kapusta.domain.com",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "invalid url, multiply wildcards",
|
|
domains: []string{"<wildcard>.<wildcard>.domain.com"},
|
|
requestURL: "s3.kapusta.dom.com",
|
|
expectedMatch: false,
|
|
},
|
|
{
|
|
name: "invalid url with invalid bktName, multiply wildcards",
|
|
domains: []string{"<wildcard>.<wildcard>.domain.com"},
|
|
requestURL: "bktA.bktB.s3.kapusta.domain.com",
|
|
expectedMatch: false,
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
bktName, match := checkDomain(tc.requestURL, tc.domains)
|
|
require.Equal(t, tc.expectedBktName, bktName)
|
|
require.Equal(t, tc.expectedMatch, match)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetDomains(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
settings := &VHSSettingsMock{
|
|
domains: []string{
|
|
"s3.domain.com",
|
|
"s3.<wildcard>.domain.com",
|
|
"domain.com",
|
|
},
|
|
}
|
|
|
|
t.Run("the request does not contain the X-Frostfs-Servername header", func(t *testing.T) {
|
|
actualDomains := getDomains(req, settings)
|
|
require.Equal(t, settings.domains, actualDomains)
|
|
})
|
|
|
|
serverName := "domain.com"
|
|
req.Header.Set(settings.ServernameHeader(), serverName)
|
|
|
|
t.Run("the request contains the X-Frostfs-Servername header", func(t *testing.T) {
|
|
actualDomains := getDomains(req, settings)
|
|
require.Equal(t, []string{serverName}, actualDomains)
|
|
})
|
|
}
|