package handler import ( "net/http" "testing" "time" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api" apierr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/stretchr/testify/require" ) func TestConditionalHead(t *testing.T) { tc := prepareHandlerContext(t) bktName, objName := "bucket-for-conditional", "object" _, objInfo := createBucketAndObject(tc, bktName, objName) w, r := prepareTestRequest(tc, bktName, objName, nil) tc.Handler().HeadObjectHandler(w, r) assertStatus(t, w, http.StatusOK) etag := w.Result().Header.Get(api.ETag) etagQuoted := "\"" + etag + "\"" headers := map[string]string{api.IfMatch: etag} headObject(t, tc, bktName, objName, headers, http.StatusOK) headers = map[string]string{api.IfMatch: etagQuoted} headObject(t, tc, bktName, objName, headers, http.StatusOK) headers = map[string]string{api.IfMatch: "etag"} headObject(t, tc, bktName, objName, headers, http.StatusPreconditionFailed) headers = map[string]string{api.IfUnmodifiedSince: objInfo.Created.Add(time.Minute).Format(http.TimeFormat)} headObject(t, tc, bktName, objName, headers, http.StatusOK) var zeroTime time.Time headers = map[string]string{api.IfUnmodifiedSince: zeroTime.UTC().Format(http.TimeFormat)} headObject(t, tc, bktName, objName, headers, http.StatusPreconditionFailed) headers = map[string]string{ api.IfMatch: etag, api.IfUnmodifiedSince: zeroTime.UTC().Format(http.TimeFormat), } headObject(t, tc, bktName, objName, headers, http.StatusOK) headers = map[string]string{api.IfNoneMatch: etag} headObject(t, tc, bktName, objName, headers, http.StatusNotModified) headers = map[string]string{api.IfNoneMatch: etagQuoted} headObject(t, tc, bktName, objName, headers, http.StatusNotModified) headers = map[string]string{api.IfNoneMatch: "etag"} headObject(t, tc, bktName, objName, headers, http.StatusOK) headers = map[string]string{api.IfModifiedSince: zeroTime.UTC().Format(http.TimeFormat)} headObject(t, tc, bktName, objName, headers, http.StatusOK) headers = map[string]string{api.IfModifiedSince: time.Now().Add(time.Minute).UTC().Format(http.TimeFormat)} headObject(t, tc, bktName, objName, headers, http.StatusNotModified) headers = map[string]string{ api.IfNoneMatch: etag, api.IfModifiedSince: zeroTime.UTC().Format(http.TimeFormat), } headObject(t, tc, bktName, objName, headers, http.StatusNotModified) headers = map[string]string{ api.IfUnmodifiedSince: zeroTime.UTC().Format(time.RFC3339), // invalid format, header is ignored } headObject(t, tc, bktName, objName, headers, http.StatusOK) headers = map[string]string{ api.IfModifiedSince: objInfo.Created.Add(time.Minute).Format(time.RFC3339), // invalid format, header is ignored } headObject(t, tc, bktName, objName, headers, http.StatusOK) } func headObject(t *testing.T, tc *handlerContext, bktName, objName string, headers map[string]string, status int) { w, r := prepareTestRequest(tc, bktName, objName, nil) for key, val := range headers { r.Header.Set(key, val) } tc.Handler().HeadObjectHandler(w, r) assertStatus(t, w, status) } func TestHeadObject(t *testing.T) { hc := prepareHandlerContextWithMinCache(t) bktName, objName := "bucket", "obj" bktInfo, objInfo := createVersionedBucketAndObject(hc.t, hc, bktName, objName) putObject(hc, bktName, objName) checkFound(hc.t, hc, bktName, objName, objInfo.VersionID()) checkFound(hc.t, hc, bktName, objName, emptyVersion) addr := getAddressOfLastVersion(hc, bktInfo, objName) hc.tp.SetObjectError(addr, &apistatus.ObjectNotFound{}) hc.tp.SetObjectError(objInfo.Address(), &apistatus.ObjectNotFound{}) headObjectAssertS3Error(hc, bktName, objName, objInfo.VersionID(), apierr.ErrNoSuchVersion) headObjectAssertS3Error(hc, bktName, objName, emptyVersion, apierr.ErrNoSuchKey) } func TestHeadObjectNotModifiedHeaders(t *testing.T) { hc := prepareHandlerContextWithMinCache(t) bktName, objName, metadataHeader := "bucket", "obj", api.MetadataPrefix+"header" createVersionedBucket(hc, bktName) header := putObjectWithHeaders(hc, bktName, objName, map[string]string{api.CacheControl: "value", metadataHeader: "value"}) etag, versionID := header.Get(api.ETag), header.Get(api.AmzVersionID) require.NotEmpty(t, etag) require.NotEmpty(t, versionID) putObjectTagging(t, hc, bktName, objName, map[string]string{"key": "value"}) w := headObjectWithHeaders(hc, bktName, objName, emptyVersion, map[string]string{api.IfNoneMatch: etag}) require.Equal(t, http.StatusNotModified, w.Code) require.Equal(t, "1", w.Header().Get(api.AmzTaggingCount)) require.Equal(t, etag, w.Header().Get(api.ETag)) require.NotEmpty(t, w.Header().Get(api.LastModified)) require.Equal(t, versionID, w.Header().Get(api.AmzVersionID)) require.Equal(t, "value", w.Header().Get(api.CacheControl)) require.Equal(t, []string{"value"}, w.Header()[metadataHeader]) } func TestIsAvailableToResolve(t *testing.T) { list := []string{"container", "s3"} for i, testCase := range [...]struct { isAllowList bool list []string zone string expected bool }{ {isAllowList: true, list: list, zone: "container", expected: true}, {isAllowList: true, list: list, zone: "sftp", expected: false}, {isAllowList: false, list: list, zone: "s3", expected: false}, {isAllowList: false, list: list, zone: "system", expected: true}, {isAllowList: true, list: list, zone: "", expected: false}, } { result := isAvailableToResolve(testCase.zone, testCase.list, testCase.isAllowList) require.Equal(t, testCase.expected, result, "case %d", i+1) } } func newTestAccessBox(key *keys.PrivateKey) (*accessbox.Box, error) { var err error if key == nil { key, err = keys.NewPrivateKey() if err != nil { return nil, err } } var btoken bearer.Token btoken.SetImpersonate(true) err = btoken.Sign(key.PrivateKey) if err != nil { return nil, err } return &accessbox.Box{ Gate: &accessbox.GateData{ BearerToken: &btoken, }, }, nil }