diff --git a/api/middleware/address_style_test.go b/api/middleware/address_style_test.go new file mode 100644 index 00000000..43b03eab --- /dev/null +++ b/api/middleware/address_style_test.go @@ -0,0 +1,385 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +type VHSSettingsMock struct { + domains []string +} + +func (v *VHSSettingsMock) Domains() []string { + return v.domains +} + +func (v *VHSSettingsMock) GlobalVHS() bool { + return false +} + +func (v *VHSSettingsMock) VHSNamespacesEnabled() map[string]bool { + return make(map[string]bool) +} + +func TestIsVHSAddress(t *testing.T) { + for _, tc := range []struct { + name 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, + }, + } { + t.Run(tc.name, func(t *testing.T) { + actual := isVHSAddress(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..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..domain.com"}, + requestURL: "s3.kapusta.domain.com", + expectedBktName: "", + expectedMatch: true, + }, + { + name: "invalid url with invalid bktName (wildcard after protocol infix)", + domains: []string{"s3..domain.com"}, + requestURL: "bktA.bktB.s3.kapusta.domain.com", + expectedMatch: false, + }, + { + name: "invalid url without namespace (wildcard after protocol infix)", + domains: []string{"s3..domain.com"}, + requestURL: "bktA.s3.domain.com", + expectedMatch: false, + }, + { + name: "invalid url with invalid infix (wildcard after protocol infix)", + domains: []string{"s3..domain.com"}, + requestURL: "bktA.s4.kapusta.domain.com", + expectedMatch: false, + }, + { + name: "invalid url with invalid postfix (wildcard after protocol infix)", + domains: []string{"s3..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{".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{".domain.com"}, + requestURL: "kapusta.domain.com", + expectedBktName: "", + expectedMatch: true, + }, + { + name: "invalid url with invalid bktName (wildcard at the beginning of the domain)", + domains: []string{".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{".domain.com"}, + requestURL: "bktA.domain.com", + expectedMatch: true, + }, + { + name: "invalid url (fewer hosts)", + domains: []string{".domain.com"}, + requestURL: "domain.com", + expectedMatch: false, + }, + { + name: "invalid url with invalid postfix (wildcard at the beginning of the domain)", + domains: []string{".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..domain.com", ".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{"..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{"..domain.com"}, + requestURL: "s3.kapusta.domain.com", + expectedBktName: "", + expectedMatch: true, + }, + { + name: "valid url with bktName, multiply wildcards", + domains: []string{"s3..subdomain..com"}, + requestURL: "bktA.s3.kapusta.subdomain.domain.com", + expectedBktName: "bktA", + expectedMatch: true, + }, + { + name: "valid url without bktName, multiply wildcards", + domains: []string{"s3..subdomain..com"}, + requestURL: "s3.kapusta.subdomain.domain.com", + expectedBktName: "", + expectedMatch: true, + }, + { + name: "invalid url without one wildcard", + domains: []string{"..domain.com"}, + requestURL: "kapusta.domain.com", + expectedMatch: false, + }, + { + name: "invalid url, multiply wildcards", + domains: []string{"..domain.com"}, + requestURL: "s3.kapusta.dom.com", + expectedMatch: false, + }, + { + name: "invalid url with invalid bktName, multiply wildcards", + domains: []string{"..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) + }) + } +}