From 6ef7e3554be4584936dd21666c3b0a87a19d80be Mon Sep 17 00:00:00 2001 From: Tatiana Nesterenko Date: Wed, 14 Aug 2024 21:04:16 +0100 Subject: [PATCH 1/7] handlers/object: add wallet auth to new GET/HEAD object requests Header parameters `X-Bearer-Signature`, `X-Bearer-Signature-Key`, and query parameters `walletConnect`, `fullBearer` were added to the new GET and HEAD object requests `/objects/{cid}/by_id/{oid}` and `/objects/{cid}/by_attribute/{oid}`. This ensures consistency of the requests using the new API, as the deprecated requests use an authorization scheme with wallet connect. Signed-off-by: Tatiana Nesterenko --- cmd/neofs-rest-gw/integration_test.go | 19 +- handlers/apiserver/rest-server.gen.go | 516 +++++++++++++++++++------- handlers/newObjects.go | 82 +++- handlers/objects.go | 72 ++-- spec/rest.yaml | 16 + 5 files changed, 525 insertions(+), 180 deletions(-) diff --git a/cmd/neofs-rest-gw/integration_test.go b/cmd/neofs-rest-gw/integration_test.go index b6f8fe0..71e59f0 100644 --- a/cmd/neofs-rest-gw/integration_test.go +++ b/cmd/neofs-rest-gw/integration_test.go @@ -2006,6 +2006,9 @@ func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID } ) + queryNew := make(url.Values) + queryNew.Add(fullBearerQuery, "true") + t.Run("head", func(t *testing.T) { objID := createObject(ctx, t, p, ownerID, cnrID, attributes, content, signer) @@ -2013,7 +2016,7 @@ func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID createTS, err := strconv.ParseInt(attrTS, 10, 64) require.NoError(t, err) - request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_id/"+objID.EncodeToString()+"?"+query.Encode(), nil) + request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_id/"+objID.EncodeToString()+"?"+queryNew.Encode(), nil) require.NoError(t, err) prepareCommonHeaders(request.Header, bearerToken) request.Header.Set("Authorization", "Bearer "+resp.Token) @@ -2066,7 +2069,7 @@ func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID createTS, err := strconv.ParseInt(attrTS, 10, 64) require.NoError(t, err) - request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_id/"+objID.EncodeToString()+"?"+query.Encode(), nil) + request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_id/"+objID.EncodeToString()+"?"+queryNew.Encode(), nil) require.NoError(t, err) prepareCommonHeaders(request.Header, bearerToken) request.Header.Set("Authorization", "Bearer "+resp.Token) @@ -2169,6 +2172,9 @@ func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Poo } ) + queryNew := make(url.Values) + queryNew.Add(fullBearerQuery, "true") + t.Run("head", func(t *testing.T) { objID := createObject(ctx, t, p, ownerID, cnrID, attributes, content, signer) @@ -2176,7 +2182,7 @@ func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Poo createTS, err := strconv.ParseInt(attrTS, 10, 64) require.NoError(t, err) - request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+fileNameAttr+"?"+query.Encode(), nil) + request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+fileNameAttr+"?"+queryNew.Encode(), nil) require.NoError(t, err) prepareCommonHeaders(request.Header, bearerToken) request.Header.Set("Authorization", "Bearer "+resp.Token) @@ -2229,7 +2235,7 @@ func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Poo createTS, err := strconv.ParseInt(attrTS, 10, 64) require.NoError(t, err) - request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+multiSegmentName+"?"+query.Encode(), nil) + request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+multiSegmentName+"?"+queryNew.Encode(), nil) require.NoError(t, err) prepareCommonHeaders(request.Header, bearerToken) request.Header.Set("Authorization", "Bearer "+resp.Token) @@ -2327,7 +2333,10 @@ func restNewObjectGetByAttribute(ctx context.Context, t *testing.T, p *pool.Pool t.Run("get", func(t *testing.T) { objID := createObject(ctx, t, p, ownerID, cnrID, attributes, content, signer) - request, err = http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+fileNameAttr+"?"+query.Encode(), nil) + queryNew := make(url.Values) + queryNew.Add(fullBearerQuery, "true") + + request, err = http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+fileNameAttr+"?"+queryNew.Encode(), nil) require.NoError(t, err) prepareCommonHeaders(request.Header, bearerToken) request.Header.Set("Authorization", "Bearer "+resp.Token) diff --git a/handlers/apiserver/rest-server.gen.go b/handlers/apiserver/rest-server.gen.go index df264ab..d7bcffa 100644 --- a/handlers/apiserver/rest-server.gen.go +++ b/handlers/apiserver/rest-server.gen.go @@ -581,26 +581,74 @@ type NewUploadContainerObjectParams struct { // NewGetByAttributeParams defines parameters for NewGetByAttribute. type NewGetByAttributeParams struct { + // WalletConnect Use wallet connect signature scheme or native NeoFS signature. + WalletConnect *SignatureScheme `form:"walletConnect,omitempty" json:"walletConnect,omitempty"` + + // FullBearer Provided bearer token is final or gate should assemble it using signature. + FullBearer *FullBearerToken `form:"fullBearer,omitempty" json:"fullBearer,omitempty"` + // Download Set the Content-Disposition header as attachment in response. This makes the browser to download object as file instead of showing it on the page. Download *string `form:"download,omitempty" json:"download,omitempty"` + + // XBearerSignature Base64 encoded signature for bearer token. + XBearerSignature *SignatureParam `json:"X-Bearer-Signature,omitempty"` + + // XBearerSignatureKey Hex encoded the public part of the key that signed the bearer token. + XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` } // NewHeadByAttributeParams defines parameters for NewHeadByAttribute. type NewHeadByAttributeParams struct { + // WalletConnect Use wallet connect signature scheme or native NeoFS signature. + WalletConnect *SignatureScheme `form:"walletConnect,omitempty" json:"walletConnect,omitempty"` + + // FullBearer Provided bearer token is final or gate should assemble it using signature. + FullBearer *FullBearerToken `form:"fullBearer,omitempty" json:"fullBearer,omitempty"` + // Download Set the Content-Disposition header as attachment in response. This makes the browser to download object as file instead of showing it on the page. Download *string `form:"download,omitempty" json:"download,omitempty"` + + // XBearerSignature Base64 encoded signature for bearer token. + XBearerSignature *SignatureParam `json:"X-Bearer-Signature,omitempty"` + + // XBearerSignatureKey Hex encoded the public part of the key that signed the bearer token. + XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` } // NewGetContainerObjectParams defines parameters for NewGetContainerObject. type NewGetContainerObjectParams struct { + // WalletConnect Use wallet connect signature scheme or native NeoFS signature. + WalletConnect *SignatureScheme `form:"walletConnect,omitempty" json:"walletConnect,omitempty"` + + // FullBearer Provided bearer token is final or gate should assemble it using signature. + FullBearer *FullBearerToken `form:"fullBearer,omitempty" json:"fullBearer,omitempty"` + // Download Set the Content-Disposition header as attachment in response. This make the browser to download object as file instead of showing it on the page. Download *string `form:"download,omitempty" json:"download,omitempty"` + + // XBearerSignature Base64 encoded signature for bearer token. + XBearerSignature *SignatureParam `json:"X-Bearer-Signature,omitempty"` + + // XBearerSignatureKey Hex encoded the public part of the key that signed the bearer token. + XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` } // NewHeadContainerObjectParams defines parameters for NewHeadContainerObject. type NewHeadContainerObjectParams struct { + // WalletConnect Use wallet connect signature scheme or native NeoFS signature. + WalletConnect *SignatureScheme `form:"walletConnect,omitempty" json:"walletConnect,omitempty"` + + // FullBearer Provided bearer token is final or gate should assemble it using signature. + FullBearer *FullBearerToken `form:"fullBearer,omitempty" json:"fullBearer,omitempty"` + // Download Set the Content-Disposition header as attachment in response. This make the browser to download object as file instead of showing it on the page. Download *string `form:"download,omitempty" json:"download,omitempty"` + + // XBearerSignature Base64 encoded signature for bearer token. + XBearerSignature *SignatureParam `json:"X-Bearer-Signature,omitempty"` + + // XBearerSignatureKey Hex encoded the public part of the key that signed the bearer token. + XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` } // SearchObjectsParams defines parameters for SearchObjects. @@ -1712,6 +1760,20 @@ func (w *ServerInterfaceWrapper) NewGetByAttribute(ctx echo.Context) error { // Parameter object where we will unmarshal all parameters from the context var params NewGetByAttributeParams + // ------------- Optional query parameter "walletConnect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "walletConnect", ctx.QueryParams(), ¶ms.WalletConnect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter walletConnect: %s", err)) + } + + // ------------- Optional query parameter "fullBearer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fullBearer", ctx.QueryParams(), ¶ms.FullBearer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fullBearer: %s", err)) + } + // ------------- Optional query parameter "download" ------------- err = runtime.BindQueryParameter("form", true, false, "download", ctx.QueryParams(), ¶ms.Download) @@ -1719,6 +1781,38 @@ func (w *ServerInterfaceWrapper) NewGetByAttribute(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter download: %s", err)) } + headers := ctx.Request().Header + // ------------- Optional header parameter "X-Bearer-Signature" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature")]; found { + var XBearerSignature SignatureParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature", valueList[0], &XBearerSignature, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature: %s", err)) + } + + params.XBearerSignature = &XBearerSignature + } + // ------------- Optional header parameter "X-Bearer-Signature-Key" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature-Key")]; found { + var XBearerSignatureKey SignatureKeyParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature-Key, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature-Key", valueList[0], &XBearerSignatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature-Key: %s", err)) + } + + params.XBearerSignatureKey = &XBearerSignatureKey + } + // Invoke the callback with all the unmarshaled arguments err = w.Handler.NewGetByAttribute(ctx, containerId, attrKey, attrVal, params) return err @@ -1757,6 +1851,20 @@ func (w *ServerInterfaceWrapper) NewHeadByAttribute(ctx echo.Context) error { // Parameter object where we will unmarshal all parameters from the context var params NewHeadByAttributeParams + // ------------- Optional query parameter "walletConnect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "walletConnect", ctx.QueryParams(), ¶ms.WalletConnect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter walletConnect: %s", err)) + } + + // ------------- Optional query parameter "fullBearer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fullBearer", ctx.QueryParams(), ¶ms.FullBearer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fullBearer: %s", err)) + } + // ------------- Optional query parameter "download" ------------- err = runtime.BindQueryParameter("form", true, false, "download", ctx.QueryParams(), ¶ms.Download) @@ -1764,6 +1872,38 @@ func (w *ServerInterfaceWrapper) NewHeadByAttribute(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter download: %s", err)) } + headers := ctx.Request().Header + // ------------- Optional header parameter "X-Bearer-Signature" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature")]; found { + var XBearerSignature SignatureParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature", valueList[0], &XBearerSignature, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature: %s", err)) + } + + params.XBearerSignature = &XBearerSignature + } + // ------------- Optional header parameter "X-Bearer-Signature-Key" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature-Key")]; found { + var XBearerSignatureKey SignatureKeyParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature-Key, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature-Key", valueList[0], &XBearerSignatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature-Key: %s", err)) + } + + params.XBearerSignatureKey = &XBearerSignatureKey + } + // Invoke the callback with all the unmarshaled arguments err = w.Handler.NewHeadByAttribute(ctx, containerId, attrKey, attrVal, params) return err @@ -1826,6 +1966,20 @@ func (w *ServerInterfaceWrapper) NewGetContainerObject(ctx echo.Context) error { // Parameter object where we will unmarshal all parameters from the context var params NewGetContainerObjectParams + // ------------- Optional query parameter "walletConnect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "walletConnect", ctx.QueryParams(), ¶ms.WalletConnect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter walletConnect: %s", err)) + } + + // ------------- Optional query parameter "fullBearer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fullBearer", ctx.QueryParams(), ¶ms.FullBearer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fullBearer: %s", err)) + } + // ------------- Optional query parameter "download" ------------- err = runtime.BindQueryParameter("form", true, false, "download", ctx.QueryParams(), ¶ms.Download) @@ -1833,6 +1987,38 @@ func (w *ServerInterfaceWrapper) NewGetContainerObject(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter download: %s", err)) } + headers := ctx.Request().Header + // ------------- Optional header parameter "X-Bearer-Signature" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature")]; found { + var XBearerSignature SignatureParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature", valueList[0], &XBearerSignature, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature: %s", err)) + } + + params.XBearerSignature = &XBearerSignature + } + // ------------- Optional header parameter "X-Bearer-Signature-Key" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature-Key")]; found { + var XBearerSignatureKey SignatureKeyParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature-Key, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature-Key", valueList[0], &XBearerSignatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature-Key: %s", err)) + } + + params.XBearerSignatureKey = &XBearerSignatureKey + } + // Invoke the callback with all the unmarshaled arguments err = w.Handler.NewGetContainerObject(ctx, containerId, objectId, params) return err @@ -1863,6 +2049,20 @@ func (w *ServerInterfaceWrapper) NewHeadContainerObject(ctx echo.Context) error // Parameter object where we will unmarshal all parameters from the context var params NewHeadContainerObjectParams + // ------------- Optional query parameter "walletConnect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "walletConnect", ctx.QueryParams(), ¶ms.WalletConnect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter walletConnect: %s", err)) + } + + // ------------- Optional query parameter "fullBearer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fullBearer", ctx.QueryParams(), ¶ms.FullBearer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fullBearer: %s", err)) + } + // ------------- Optional query parameter "download" ------------- err = runtime.BindQueryParameter("form", true, false, "download", ctx.QueryParams(), ¶ms.Download) @@ -1870,6 +2070,38 @@ func (w *ServerInterfaceWrapper) NewHeadContainerObject(ctx echo.Context) error return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter download: %s", err)) } + headers := ctx.Request().Header + // ------------- Optional header parameter "X-Bearer-Signature" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature")]; found { + var XBearerSignature SignatureParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature", valueList[0], &XBearerSignature, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature: %s", err)) + } + + params.XBearerSignature = &XBearerSignature + } + // ------------- Optional header parameter "X-Bearer-Signature-Key" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature-Key")]; found { + var XBearerSignatureKey SignatureKeyParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature-Key, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature-Key", valueList[0], &XBearerSignatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature-Key: %s", err)) + } + + params.XBearerSignatureKey = &XBearerSignatureKey + } + // Invoke the callback with all the unmarshaled arguments err = w.Handler.NewHeadContainerObject(ctx, containerId, objectId, params) return err @@ -2335,148 +2567,148 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "AAD//1b7aih00gAA", + "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", + "/l7jVaN+1zQePj78XwAAAP//rWgJGKjVAAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/handlers/newObjects.go b/handlers/newObjects.go index 5d3b322..e83c5f5 100644 --- a/handlers/newObjects.go +++ b/handlers/newObjects.go @@ -144,7 +144,24 @@ func (a *RestAPI) NewGetContainerObject(ctx echo.Context, containerID apiserver. return ctx.JSON(http.StatusBadRequest, resp) } - return a.getByAddress(ctx, addr, params.Download, principal, true) + var ( + fullBearer apiserver.FullBearerToken + walletConnect apiserver.SignatureScheme + ) + if params.FullBearer != nil { + fullBearer = *params.FullBearer + } + if params.WalletConnect != nil { + walletConnect = *params.WalletConnect + } + + btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, walletConnect, fullBearer) + if err != nil { + resp := a.logAndGetErrorResponse("get bearer token", err) + return ctx.JSON(http.StatusBadRequest, resp) + } + + return a.getByAddress(ctx, addr, params.Download, btoken, true) } // NewHeadContainerObject handler that returns object info (using container ID and object ID). @@ -161,7 +178,25 @@ func (a *RestAPI) NewHeadContainerObject(ctx echo.Context, containerID apiserver } ctx.Response().Header().Set(accessControlAllowOriginHeader, "*") - return a.headByAddress(ctx, addr, params.Download, principal, true) + + var ( + fullBearer apiserver.FullBearerToken + walletConnect apiserver.SignatureScheme + ) + if params.FullBearer != nil { + fullBearer = *params.FullBearer + } + if params.WalletConnect != nil { + walletConnect = *params.WalletConnect + } + + btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, walletConnect, fullBearer) + if err != nil { + resp := a.logAndGetErrorResponse("get bearer token", err) + return ctx.JSON(http.StatusBadRequest, resp) + } + + return a.headByAddress(ctx, addr, params.Download, btoken, true) } // NewGetByAttribute handler that returns object (payload and attributes) by a specific attribute. @@ -177,7 +212,24 @@ func (a *RestAPI) NewGetByAttribute(ctx echo.Context, containerID apiserver.Cont return ctx.JSON(http.StatusBadRequest, resp) } - res, err := a.search(ctx.Request().Context(), principal, cnrID, attrKey, attrVal, object.MatchStringEqual) + var ( + fullBearer apiserver.FullBearerToken + walletConnect apiserver.SignatureScheme + ) + if params.FullBearer != nil { + fullBearer = *params.FullBearer + } + if params.WalletConnect != nil { + walletConnect = *params.WalletConnect + } + + btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, walletConnect, fullBearer) + if err != nil { + resp := a.logAndGetErrorResponse("get bearer token", err) + return ctx.JSON(http.StatusBadRequest, resp) + } + + res, err := a.search(ctx.Request().Context(), btoken, cnrID, attrKey, attrVal, object.MatchStringEqual) if err != nil { resp := a.logAndGetErrorResponse("could not search for objects", err) return ctx.JSON(http.StatusNotFound, resp) @@ -207,7 +259,7 @@ func (a *RestAPI) NewGetByAttribute(ctx echo.Context, containerID apiserver.Cont addrObj.SetContainer(cnrID) addrObj.SetObject(buf[0]) - return a.getByAddress(ctx, addrObj, params.Download, principal, true) + return a.getByAddress(ctx, addrObj, params.Download, btoken, true) } // NewHeadByAttribute handler that returns object info (payload and attributes) by a specific attribute. @@ -223,7 +275,24 @@ func (a *RestAPI) NewHeadByAttribute(ctx echo.Context, containerID apiserver.Con return ctx.JSON(http.StatusBadRequest, resp) } - res, err := a.search(ctx.Request().Context(), principal, cnrID, attrKey, attrVal, object.MatchStringEqual) + var ( + fullBearer apiserver.FullBearerToken + walletConnect apiserver.SignatureScheme + ) + if params.FullBearer != nil { + fullBearer = *params.FullBearer + } + if params.WalletConnect != nil { + walletConnect = *params.WalletConnect + } + + btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, walletConnect, fullBearer) + if err != nil { + resp := a.logAndGetErrorResponse("get bearer token", err) + return ctx.JSON(http.StatusBadRequest, resp) + } + + res, err := a.search(ctx.Request().Context(), btoken, cnrID, attrKey, attrVal, object.MatchStringEqual) if err != nil { resp := a.logAndGetErrorResponse("could not search for objects", err) return ctx.JSON(http.StatusNotFound, resp) @@ -254,5 +323,6 @@ func (a *RestAPI) NewHeadByAttribute(ctx echo.Context, containerID apiserver.Con addrObj.SetObject(buf[0]) ctx.Response().Header().Set(accessControlAllowOriginHeader, "*") - return a.headByAddress(ctx, addrObj, params.Download, principal, true) + + return a.headByAddress(ctx, addrObj, params.Download, btoken, true) } diff --git a/handlers/objects.go b/handlers/objects.go index 458a396..45d32cd 100644 --- a/handlers/objects.go +++ b/handlers/objects.go @@ -399,18 +399,21 @@ func (a *RestAPI) GetContainerObject(ctx echo.Context, containerID apiserver.Con return ctx.JSON(http.StatusBadRequest, resp) } - return a.getByAddress(ctx, addr, params.Download, principal, false) -} - -// getByAddress returns object (using container ID and object ID). -func (a *RestAPI) getByAddress(ctx echo.Context, addr oid.Address, downloadParam *string, principal string, useJSON bool) error { - var prm client.PrmObjectGet + var btoken *bearer.Token if principal != "" { - btoken, err := getBearerTokenFromString(principal) + btoken, err = getBearerTokenFromString(principal) if err != nil { resp := a.logAndGetErrorResponse("get bearer token", err) return ctx.JSON(http.StatusBadRequest, resp) } + } + return a.getByAddress(ctx, addr, params.Download, btoken, false) +} + +// getByAddress returns object (using container ID and object ID). +func (a *RestAPI) getByAddress(ctx echo.Context, addr oid.Address, downloadParam *string, btoken *bearer.Token, useJSON bool) error { + var prm client.PrmObjectGet + if btoken != nil { attachBearer(&prm, btoken) } @@ -481,23 +484,23 @@ func (a *RestAPI) HeadContainerObject(ctx echo.Context, containerID apiserver.Co } ctx.Response().Header().Set(accessControlAllowOriginHeader, "*") - return a.headByAddress(ctx, addr, params.Download, principal, false) -} - -// headByAddress returns object info (using container ID and object ID). -func (a *RestAPI) headByAddress(ctx echo.Context, addr oid.Address, downloadParam *string, principal string, useJSON bool) error { - var ( - prm client.PrmObjectHead - btoken *bearer.Token - err error - ) + var btoken *bearer.Token if principal != "" { btoken, err = getBearerTokenFromString(principal) if err != nil { resp := a.logAndGetErrorResponse("get bearer token", err) return ctx.JSON(http.StatusBadRequest, resp) } + } + + return a.headByAddress(ctx, addr, params.Download, btoken, false) +} + +// headByAddress returns object info (using container ID and object ID). +func (a *RestAPI) headByAddress(ctx echo.Context, addr oid.Address, downloadParam *string, btoken *bearer.Token, useJSON bool) error { + var prm client.PrmObjectHead + if btoken != nil { attachBearer(&prm, btoken) } @@ -1050,7 +1053,16 @@ func (a *RestAPI) GetByAttribute(ctx echo.Context, containerID apiserver.Contain return ctx.JSON(http.StatusBadRequest, resp) } - res, err := a.search(ctx.Request().Context(), principal, cnrID, attrKey, attrVal, object.MatchStringEqual) + var btoken *bearer.Token + if principal != "" { + btoken, err = getBearerTokenFromString(principal) + if err != nil { + resp := a.logAndGetErrorResponse("get bearer token", err) + return ctx.JSON(http.StatusBadRequest, resp) + } + } + + res, err := a.search(ctx.Request().Context(), btoken, cnrID, attrKey, attrVal, object.MatchStringEqual) if err != nil { resp := a.logAndGetErrorResponse("could not search for objects", err) return ctx.JSON(http.StatusNotFound, resp) @@ -1080,7 +1092,7 @@ func (a *RestAPI) GetByAttribute(ctx echo.Context, containerID apiserver.Contain addrObj.SetContainer(cnrID) addrObj.SetObject(buf[0]) - return a.getByAddress(ctx, addrObj, params.Download, principal, false) + return a.getByAddress(ctx, addrObj, params.Download, btoken, false) } // HeadByAttribute handler that returns object info (payload and attributes) by a specific attribute. @@ -1096,7 +1108,16 @@ func (a *RestAPI) HeadByAttribute(ctx echo.Context, containerID apiserver.Contai return ctx.JSON(http.StatusBadRequest, resp) } - res, err := a.search(ctx.Request().Context(), principal, cnrID, attrKey, attrVal, object.MatchStringEqual) + var btoken *bearer.Token + if principal != "" { + btoken, err = getBearerTokenFromString(principal) + if err != nil { + resp := a.logAndGetErrorResponse("get bearer token", err) + return ctx.JSON(http.StatusBadRequest, resp) + } + } + + res, err := a.search(ctx.Request().Context(), btoken, cnrID, attrKey, attrVal, object.MatchStringEqual) if err != nil { resp := a.logAndGetErrorResponse("could not search for objects", err) return ctx.JSON(http.StatusNotFound, resp) @@ -1127,10 +1148,11 @@ func (a *RestAPI) HeadByAttribute(ctx echo.Context, containerID apiserver.Contai addrObj.SetObject(buf[0]) ctx.Response().Header().Set(accessControlAllowOriginHeader, "*") - return a.headByAddress(ctx, addrObj, params.Download, principal, false) + + return a.headByAddress(ctx, addrObj, params.Download, btoken, false) } -func (a *RestAPI) search(ctx context.Context, principal string, cid cid.ID, key, val string, op object.SearchMatchType) (*client.ObjectListReader, error) { +func (a *RestAPI) search(ctx context.Context, btoken *bearer.Token, cid cid.ID, key, val string, op object.SearchMatchType) (*client.ObjectListReader, error) { filters := object.NewSearchFilters() filters.AddRootFilter() filters.AddFilter(key, val, op) @@ -1138,11 +1160,7 @@ func (a *RestAPI) search(ctx context.Context, principal string, cid cid.ID, key, var prm client.PrmObjectSearch prm.SetFilters(filters) - if principal != "" { - btoken, err := getBearerTokenFromString(principal) - if err != nil { - return nil, err - } + if btoken != nil { attachBearer(&prm, btoken) } diff --git a/spec/rest.yaml b/spec/rest.yaml index 732f506..91ee63b 100644 --- a/spec/rest.yaml +++ b/spec/rest.yaml @@ -761,6 +761,10 @@ paths: parameters: - $ref: '#/components/parameters/containerId' - $ref: '#/components/parameters/objectId' + - $ref: '#/components/parameters/signatureParam' + - $ref: '#/components/parameters/signatureKeyParam' + - $ref: '#/components/parameters/signatureScheme' + - $ref: '#/components/parameters/fullBearerToken' - name: download in: query description: Set the Content-Disposition header as attachment in response. @@ -815,6 +819,10 @@ paths: parameters: - $ref: '#/components/parameters/containerId' - $ref: '#/components/parameters/objectId' + - $ref: '#/components/parameters/signatureParam' + - $ref: '#/components/parameters/signatureKeyParam' + - $ref: '#/components/parameters/signatureScheme' + - $ref: '#/components/parameters/fullBearerToken' - name: download in: query description: Set the Content-Disposition header as attachment in response. @@ -1121,6 +1129,10 @@ paths: - $ref: '#/components/parameters/containerId' - $ref: '#/components/parameters/attrKey' - $ref: '#/components/parameters/attrVal' + - $ref: '#/components/parameters/signatureParam' + - $ref: '#/components/parameters/signatureKeyParam' + - $ref: '#/components/parameters/signatureScheme' + - $ref: '#/components/parameters/fullBearerToken' - name: download in: query description: Set the Content-Disposition header as attachment in response. @@ -1179,6 +1191,10 @@ paths: - $ref: '#/components/parameters/containerId' - $ref: '#/components/parameters/attrKey' - $ref: '#/components/parameters/attrVal' + - $ref: '#/components/parameters/signatureParam' + - $ref: '#/components/parameters/signatureKeyParam' + - $ref: '#/components/parameters/signatureScheme' + - $ref: '#/components/parameters/fullBearerToken' - name: download in: query description: Set the Content-Disposition header as attachment in response. From fefdfb6798eb6899070649d103dfefa5835018c3 Mon Sep 17 00:00:00 2001 From: Tatiana Nesterenko Date: Wed, 14 Aug 2024 21:35:34 +0100 Subject: [PATCH 2/7] handlers/object: update allowed headers in the OPTIONS request Signed-off-by: Tatiana Nesterenko --- handlers/preflight.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/handlers/preflight.go b/handlers/preflight.go index e0dc993..53e7891 100644 --- a/handlers/preflight.go +++ b/handlers/preflight.go @@ -9,8 +9,11 @@ import ( ) const ( - allOrigins = "*" - allowHeaders = "X-Bearer-For-All-Users, X-Bearer-Lifetime, X-Bearer-Owner-Id, X-Bearer-Signature, X-Bearer-Signature-Key, Content-Type, Authorization" + allOrigins = "*" + allowHeaders = "X-Bearer-For-All-Users, X-Bearer-Lifetime, X-Bearer-Owner-Id, " + + "X-Bearer-Signature, X-Bearer-Signature-Key, Content-Type, Authorization, " + + "X-Attribute-Filename, X-Attribute-Filepath, X-Attributes, " + + "X-Neofs-Expiration-RFC3339, X-Neofs-Expiration-Timestamp, X-Neofs-Expiration-Duration" allowUploadHeader = "*" methodGet = "GET" From 0b10d987e7c625c01f8488aa31c003bb5e1109a2 Mon Sep 17 00:00:00 2001 From: Tatiana Nesterenko Date: Wed, 14 Aug 2024 21:37:19 +0100 Subject: [PATCH 3/7] handlers/object: update OPTIONS params for object requests `by_id` and `by_attribute` Signed-off-by: Tatiana Nesterenko --- handlers/apiserver/rest-server.gen.go | 418 +++++++++++++++++--------- handlers/preflight.go | 4 +- spec/rest.yaml | 8 + 3 files changed, 282 insertions(+), 148 deletions(-) diff --git a/handlers/apiserver/rest-server.gen.go b/handlers/apiserver/rest-server.gen.go index d7bcffa..1addd00 100644 --- a/handlers/apiserver/rest-server.gen.go +++ b/handlers/apiserver/rest-server.gen.go @@ -615,6 +615,21 @@ type NewHeadByAttributeParams struct { XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` } +// NewOptionsByAttributeParams defines parameters for NewOptionsByAttribute. +type NewOptionsByAttributeParams struct { + // WalletConnect Use wallet connect signature scheme or native NeoFS signature. + WalletConnect *SignatureScheme `form:"walletConnect,omitempty" json:"walletConnect,omitempty"` + + // FullBearer Provided bearer token is final or gate should assemble it using signature. + FullBearer *FullBearerToken `form:"fullBearer,omitempty" json:"fullBearer,omitempty"` + + // XBearerSignature Base64 encoded signature for bearer token. + XBearerSignature *SignatureParam `json:"X-Bearer-Signature,omitempty"` + + // XBearerSignatureKey Hex encoded the public part of the key that signed the bearer token. + XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` +} + // NewGetContainerObjectParams defines parameters for NewGetContainerObject. type NewGetContainerObjectParams struct { // WalletConnect Use wallet connect signature scheme or native NeoFS signature. @@ -651,6 +666,21 @@ type NewHeadContainerObjectParams struct { XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` } +// NewOptionsContainerObjectParams defines parameters for NewOptionsContainerObject. +type NewOptionsContainerObjectParams struct { + // WalletConnect Use wallet connect signature scheme or native NeoFS signature. + WalletConnect *SignatureScheme `form:"walletConnect,omitempty" json:"walletConnect,omitempty"` + + // FullBearer Provided bearer token is final or gate should assemble it using signature. + FullBearer *FullBearerToken `form:"fullBearer,omitempty" json:"fullBearer,omitempty"` + + // XBearerSignature Base64 encoded signature for bearer token. + XBearerSignature *SignatureParam `json:"X-Bearer-Signature,omitempty"` + + // XBearerSignatureKey Hex encoded the public part of the key that signed the bearer token. + XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` +} + // SearchObjectsParams defines parameters for SearchObjects. type SearchObjectsParams struct { // WalletConnect Use wallet connect signature scheme or native NeoFS signature. @@ -827,7 +857,7 @@ type ServerInterface interface { NewHeadByAttribute(ctx echo.Context, containerId ContainerId, attrKey AttrKey, attrVal AttrVal, params NewHeadByAttributeParams) error // (OPTIONS /objects/{containerId}/by_attribute/{attrKey}/{attrVal}) - NewOptionsByAttribute(ctx echo.Context, containerId ContainerId, attrKey AttrKey, attrVal AttrVal) error + NewOptionsByAttribute(ctx echo.Context, containerId ContainerId, attrKey AttrKey, attrVal AttrVal, params NewOptionsByAttributeParams) error // Get object by container ID and object ID. Also, returns custom users' object attributes in header `X-Attributes`. It returns the MIME type based on headers or object contents, so the actual Content-Type can differ from the list in the "Response content type" section. // (GET /objects/{containerId}/by_id/{objectId}) NewGetContainerObject(ctx echo.Context, containerId ContainerId, objectId ObjectId, params NewGetContainerObjectParams) error @@ -836,7 +866,7 @@ type ServerInterface interface { NewHeadContainerObject(ctx echo.Context, containerId ContainerId, objectId ObjectId, params NewHeadContainerObjectParams) error // (OPTIONS /objects/{containerId}/by_id/{objectId}) - NewOptionsContainerObject(ctx echo.Context, containerId ContainerId, objectId ObjectId) error + NewOptionsContainerObject(ctx echo.Context, containerId ContainerId, objectId ObjectId, params NewOptionsContainerObjectParams) error // (OPTIONS /objects/{containerId}/search) OptionsObjectsSearch(ctx echo.Context, containerId string) error @@ -1936,8 +1966,56 @@ func (w *ServerInterfaceWrapper) NewOptionsByAttribute(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter attrVal: %s", err)) } + // Parameter object where we will unmarshal all parameters from the context + var params NewOptionsByAttributeParams + // ------------- Optional query parameter "walletConnect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "walletConnect", ctx.QueryParams(), ¶ms.WalletConnect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter walletConnect: %s", err)) + } + + // ------------- Optional query parameter "fullBearer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fullBearer", ctx.QueryParams(), ¶ms.FullBearer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fullBearer: %s", err)) + } + + headers := ctx.Request().Header + // ------------- Optional header parameter "X-Bearer-Signature" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature")]; found { + var XBearerSignature SignatureParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature", valueList[0], &XBearerSignature, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature: %s", err)) + } + + params.XBearerSignature = &XBearerSignature + } + // ------------- Optional header parameter "X-Bearer-Signature-Key" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature-Key")]; found { + var XBearerSignatureKey SignatureKeyParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature-Key, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature-Key", valueList[0], &XBearerSignatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature-Key: %s", err)) + } + + params.XBearerSignatureKey = &XBearerSignatureKey + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.NewOptionsByAttribute(ctx, containerId, attrKey, attrVal) + err = w.Handler.NewOptionsByAttribute(ctx, containerId, attrKey, attrVal, params) return err } @@ -2126,8 +2204,56 @@ func (w *ServerInterfaceWrapper) NewOptionsContainerObject(ctx echo.Context) err return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter objectId: %s", err)) } + // Parameter object where we will unmarshal all parameters from the context + var params NewOptionsContainerObjectParams + // ------------- Optional query parameter "walletConnect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "walletConnect", ctx.QueryParams(), ¶ms.WalletConnect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter walletConnect: %s", err)) + } + + // ------------- Optional query parameter "fullBearer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fullBearer", ctx.QueryParams(), ¶ms.FullBearer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fullBearer: %s", err)) + } + + headers := ctx.Request().Header + // ------------- Optional header parameter "X-Bearer-Signature" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature")]; found { + var XBearerSignature SignatureParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature", valueList[0], &XBearerSignature, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature: %s", err)) + } + + params.XBearerSignature = &XBearerSignature + } + // ------------- Optional header parameter "X-Bearer-Signature-Key" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature-Key")]; found { + var XBearerSignatureKey SignatureKeyParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature-Key, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature-Key", valueList[0], &XBearerSignatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature-Key: %s", err)) + } + + params.XBearerSignatureKey = &XBearerSignatureKey + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.NewOptionsContainerObject(ctx, containerId, objectId) + err = w.Handler.NewOptionsContainerObject(ctx, containerId, objectId, params) return err } @@ -2567,148 +2693,148 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/l7jVaN+1zQePj78XwAAAP//rWgJGKjVAAA=", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/handlers/preflight.go b/handlers/preflight.go index 53e7891..420e53a 100644 --- a/handlers/preflight.go +++ b/handlers/preflight.go @@ -146,7 +146,7 @@ func (a *RestAPI) NewOptionsUploadContainerObject(ctx echo.Context, _ apiserver. } // NewOptionsContainerObject handler for the create object options request. -func (a *RestAPI) NewOptionsContainerObject(ctx echo.Context, _ apiserver.ContainerId, _ apiserver.ObjectId) error { +func (a *RestAPI) NewOptionsContainerObject(ctx echo.Context, _ apiserver.ContainerId, _ apiserver.ObjectId, _ apiserver.NewOptionsContainerObjectParams) error { ctx.Response().Header().Set(accessControlAllowOriginHeader, allOrigins) ctx.Response().Header().Set(accessControlAllowHeadersHeader, allowHeaders) ctx.Response().Header().Set(accessControlAllowMethodsHeader, allowMethods(methodGet, methodHead)) @@ -154,7 +154,7 @@ func (a *RestAPI) NewOptionsContainerObject(ctx echo.Context, _ apiserver.Contai } // NewOptionsByAttribute handler for the find by attribute options request. -func (a *RestAPI) NewOptionsByAttribute(ctx echo.Context, _ apiserver.ContainerId, _ apiserver.AttrKey, _ apiserver.AttrVal) error { +func (a *RestAPI) NewOptionsByAttribute(ctx echo.Context, _ apiserver.ContainerId, _ apiserver.AttrKey, _ apiserver.AttrVal, _ apiserver.NewOptionsByAttributeParams) error { ctx.Response().Header().Set(accessControlAllowOriginHeader, allOrigins) ctx.Response().Header().Set(accessControlAllowHeadersHeader, allowHeaders) ctx.Response().Header().Set(accessControlAllowMethodsHeader, allowMethods(methodGet, methodHead)) diff --git a/spec/rest.yaml b/spec/rest.yaml index 91ee63b..6049321 100644 --- a/spec/rest.yaml +++ b/spec/rest.yaml @@ -797,6 +797,10 @@ paths: parameters: - $ref: '#/components/parameters/containerId' - $ref: '#/components/parameters/objectId' + - $ref: '#/components/parameters/signatureParam' + - $ref: '#/components/parameters/signatureKeyParam' + - $ref: '#/components/parameters/signatureScheme' + - $ref: '#/components/parameters/fullBearerToken' responses: "200": description: CORS @@ -1166,6 +1170,10 @@ paths: - $ref: '#/components/parameters/containerId' - $ref: '#/components/parameters/attrKey' - $ref: '#/components/parameters/attrVal' + - $ref: '#/components/parameters/signatureParam' + - $ref: '#/components/parameters/signatureKeyParam' + - $ref: '#/components/parameters/signatureScheme' + - $ref: '#/components/parameters/fullBearerToken' responses: "200": description: CORS From 4262e55b605672a8c4182661bd53d3137c2a8367 Mon Sep 17 00:00:00 2001 From: Tatiana Nesterenko Date: Wed, 14 Aug 2024 21:57:58 +0100 Subject: [PATCH 4/7] handlers/object: add wallet auth for object upload Header parameters `X-Bearer-Signature`, `X-Bearer-Signature-Key`, and query parameters `walletConnect`, `fullBearer` were added to the new POST object requests `/objects/{cid}`. This ensures consistency of the requests using the new API, as the deprecated requests use an authorization scheme with wallet connect. Signed-off-by: Tatiana Nesterenko --- cmd/neofs-rest-gw/integration_test.go | 5 +- handlers/apiserver/rest-server.gen.go | 339 +++++++++++++++----------- handlers/newObjects.go | 25 +- spec/rest.yaml | 4 + 4 files changed, 221 insertions(+), 152 deletions(-) diff --git a/cmd/neofs-rest-gw/integration_test.go b/cmd/neofs-rest-gw/integration_test.go index 71e59f0..9b8e511 100644 --- a/cmd/neofs-rest-gw/integration_test.go +++ b/cmd/neofs-rest-gw/integration_test.go @@ -1917,8 +1917,11 @@ func restNewObjectUploadInt(ctx context.Context, t *testing.T, clientPool *pool. attributesJSON, err := json.Marshal(attributes) require.NoError(t, err) + queryNew := make(url.Values) + queryNew.Add(fullBearerQuery, "true") + body := bytes.NewBufferString(content) - request, err = http.NewRequest(http.MethodPost, testHost+"/v1/objects/"+cnrID.String(), body) + request, err = http.NewRequest(http.MethodPost, testHost+"/v1/objects/"+cnrID.String()+"?"+queryNew.Encode(), body) require.NoError(t, err) request.Header.Set("Content-Type", "text/plain") diff --git a/handlers/apiserver/rest-server.gen.go b/handlers/apiserver/rest-server.gen.go index 1addd00..2d4ceaf 100644 --- a/handlers/apiserver/rest-server.gen.go +++ b/handlers/apiserver/rest-server.gen.go @@ -553,6 +553,18 @@ type PutObjectParams struct { // NewUploadContainerObjectParams defines parameters for NewUploadContainerObject. type NewUploadContainerObjectParams struct { + // WalletConnect Use wallet connect signature scheme or native NeoFS signature. + WalletConnect *SignatureScheme `form:"walletConnect,omitempty" json:"walletConnect,omitempty"` + + // FullBearer Provided bearer token is final or gate should assemble it using signature. + FullBearer *FullBearerToken `form:"fullBearer,omitempty" json:"fullBearer,omitempty"` + + // XBearerSignature Base64 encoded signature for bearer token. + XBearerSignature *SignatureParam `json:"X-Bearer-Signature,omitempty"` + + // XBearerSignatureKey Hex encoded the public part of the key that signed the bearer token. + XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` + // XAttributes All attributes are in a JSON-formatted map of key-value pairs, where the key is the // attribute name and the value is the attribute value. // You can also use the special attribute: @@ -1689,8 +1701,51 @@ func (w *ServerInterfaceWrapper) NewUploadContainerObject(ctx echo.Context) erro // Parameter object where we will unmarshal all parameters from the context var params NewUploadContainerObjectParams + // ------------- Optional query parameter "walletConnect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "walletConnect", ctx.QueryParams(), ¶ms.WalletConnect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter walletConnect: %s", err)) + } + + // ------------- Optional query parameter "fullBearer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fullBearer", ctx.QueryParams(), ¶ms.FullBearer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fullBearer: %s", err)) + } headers := ctx.Request().Header + // ------------- Optional header parameter "X-Bearer-Signature" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature")]; found { + var XBearerSignature SignatureParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature", valueList[0], &XBearerSignature, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature: %s", err)) + } + + params.XBearerSignature = &XBearerSignature + } + // ------------- Optional header parameter "X-Bearer-Signature-Key" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature-Key")]; found { + var XBearerSignatureKey SignatureKeyParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature-Key, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature-Key", valueList[0], &XBearerSignatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature-Key: %s", err)) + } + + params.XBearerSignatureKey = &XBearerSignatureKey + } // ------------- Optional header parameter "X-Attributes" ------------- if valueList, found := headers[http.CanonicalHeaderKey("X-Attributes")]; found { var XAttributes string @@ -2693,148 +2748,148 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/handlers/newObjects.go b/handlers/newObjects.go index e83c5f5..c50c54c 100644 --- a/handlers/newObjects.go +++ b/handlers/newObjects.go @@ -20,11 +20,20 @@ import ( // NewUploadContainerObject handler that upload file as object with attributes to NeoFS. func (a *RestAPI) NewUploadContainerObject(ctx echo.Context, containerID apiserver.ContainerId, params apiserver.NewUploadContainerObjectParams) error { var ( - err error - addr oid.Address - btoken *bearer.Token + err error + addr oid.Address + btoken *bearer.Token + fullBearer apiserver.FullBearerToken + walletConnect apiserver.SignatureScheme ) + if params.FullBearer != nil { + fullBearer = *params.FullBearer + } + if params.WalletConnect != nil { + walletConnect = *params.WalletConnect + } + var idCnr cid.ID if err = idCnr.DecodeString(containerID); err != nil { resp := a.logAndGetErrorResponse("invalid container id", err) @@ -36,12 +45,10 @@ func (a *RestAPI) NewUploadContainerObject(ctx echo.Context, containerID apiserv return ctx.JSON(http.StatusBadRequest, util.NewErrorResponse(err)) } - if principal != "" { - btoken, err = getBearerTokenFromString(principal) - if err != nil { - resp := a.logAndGetErrorResponse("get bearer token", err) - return ctx.JSON(http.StatusBadRequest, resp) - } + btoken, err = getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, walletConnect, fullBearer) + if err != nil { + resp := a.logAndGetErrorResponse("invalid bearer token", err) + return ctx.JSON(http.StatusBadRequest, resp) } filtered, err := parseAndFilterAttributes(a.log, params.XAttributes) diff --git a/spec/rest.yaml b/spec/rest.yaml index 6049321..7602627 100644 --- a/spec/rest.yaml +++ b/spec/rest.yaml @@ -936,6 +936,10 @@ paths: operationId: newUploadContainerObject parameters: - $ref: '#/components/parameters/containerId' + - $ref: '#/components/parameters/signatureParam' + - $ref: '#/components/parameters/signatureKeyParam' + - $ref: '#/components/parameters/signatureScheme' + - $ref: '#/components/parameters/fullBearerToken' - name: X-Attributes in: header description: | From ab7519975697a0c711483cc1a60b71e22b743a6f Mon Sep 17 00:00:00 2001 From: Tatiana Nesterenko Date: Wed, 14 Aug 2024 22:05:18 +0100 Subject: [PATCH 5/7] handlers/object: update OPTIONS params for the new object upload Signed-off-by: Tatiana Nesterenko --- handlers/apiserver/rest-server.gen.go | 351 +++++++++++++++----------- handlers/preflight.go | 2 +- spec/rest.yaml | 4 + 3 files changed, 212 insertions(+), 145 deletions(-) diff --git a/handlers/apiserver/rest-server.gen.go b/handlers/apiserver/rest-server.gen.go index 2d4ceaf..bec7799 100644 --- a/handlers/apiserver/rest-server.gen.go +++ b/handlers/apiserver/rest-server.gen.go @@ -551,6 +551,21 @@ type PutObjectParams struct { XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` } +// NewOptionsUploadContainerObjectParams defines parameters for NewOptionsUploadContainerObject. +type NewOptionsUploadContainerObjectParams struct { + // WalletConnect Use wallet connect signature scheme or native NeoFS signature. + WalletConnect *SignatureScheme `form:"walletConnect,omitempty" json:"walletConnect,omitempty"` + + // FullBearer Provided bearer token is final or gate should assemble it using signature. + FullBearer *FullBearerToken `form:"fullBearer,omitempty" json:"fullBearer,omitempty"` + + // XBearerSignature Base64 encoded signature for bearer token. + XBearerSignature *SignatureParam `json:"X-Bearer-Signature,omitempty"` + + // XBearerSignatureKey Hex encoded the public part of the key that signed the bearer token. + XBearerSignatureKey *SignatureKeyParam `json:"X-Bearer-Signature-Key,omitempty"` +} + // NewUploadContainerObjectParams defines parameters for NewUploadContainerObject. type NewUploadContainerObjectParams struct { // WalletConnect Use wallet connect signature scheme or native NeoFS signature. @@ -857,7 +872,7 @@ type ServerInterface interface { PutObject(ctx echo.Context, params PutObjectParams) error // (OPTIONS /objects/{containerId}) - NewOptionsUploadContainerObject(ctx echo.Context, containerId ContainerId) error + NewOptionsUploadContainerObject(ctx echo.Context, containerId ContainerId, params NewOptionsUploadContainerObjectParams) error // Upload object to NeoFS // (POST /objects/{containerId}) NewUploadContainerObject(ctx echo.Context, containerId ContainerId, params NewUploadContainerObjectParams) error @@ -1679,8 +1694,56 @@ func (w *ServerInterfaceWrapper) NewOptionsUploadContainerObject(ctx echo.Contex return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter containerId: %s", err)) } + // Parameter object where we will unmarshal all parameters from the context + var params NewOptionsUploadContainerObjectParams + // ------------- Optional query parameter "walletConnect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "walletConnect", ctx.QueryParams(), ¶ms.WalletConnect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter walletConnect: %s", err)) + } + + // ------------- Optional query parameter "fullBearer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fullBearer", ctx.QueryParams(), ¶ms.FullBearer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fullBearer: %s", err)) + } + + headers := ctx.Request().Header + // ------------- Optional header parameter "X-Bearer-Signature" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature")]; found { + var XBearerSignature SignatureParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature", valueList[0], &XBearerSignature, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature: %s", err)) + } + + params.XBearerSignature = &XBearerSignature + } + // ------------- Optional header parameter "X-Bearer-Signature-Key" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-Bearer-Signature-Key")]; found { + var XBearerSignatureKey SignatureKeyParam + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Bearer-Signature-Key, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-Bearer-Signature-Key", valueList[0], &XBearerSignatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Bearer-Signature-Key: %s", err)) + } + + params.XBearerSignatureKey = &XBearerSignatureKey + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.NewOptionsUploadContainerObject(ctx, containerId) + err = w.Handler.NewOptionsUploadContainerObject(ctx, containerId, params) return err } @@ -2748,148 +2811,148 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/handlers/preflight.go b/handlers/preflight.go index 420e53a..4367144 100644 --- a/handlers/preflight.go +++ b/handlers/preflight.go @@ -138,7 +138,7 @@ func (a *RestAPI) OptionsNetworkInfo(ctx echo.Context) error { } // NewOptionsUploadContainerObject handler for the upload object options request. -func (a *RestAPI) NewOptionsUploadContainerObject(ctx echo.Context, _ apiserver.ContainerId) error { +func (a *RestAPI) NewOptionsUploadContainerObject(ctx echo.Context, _ apiserver.ContainerId, _ apiserver.NewOptionsUploadContainerObjectParams) error { ctx.Response().Header().Set(accessControlAllowOriginHeader, allOrigins) ctx.Response().Header().Set(accessControlAllowHeadersHeader, allowUploadHeader) ctx.Response().Header().Set(accessControlAllowMethodsHeader, allowMethods(methodPost)) diff --git a/spec/rest.yaml b/spec/rest.yaml index 7602627..3f4c052 100644 --- a/spec/rest.yaml +++ b/spec/rest.yaml @@ -1011,6 +1011,10 @@ paths: operationId: newOptionsUploadContainerObject parameters: - $ref: '#/components/parameters/containerId' + - $ref: '#/components/parameters/signatureParam' + - $ref: '#/components/parameters/signatureKeyParam' + - $ref: '#/components/parameters/signatureScheme' + - $ref: '#/components/parameters/fullBearerToken' responses: "200": description: CORS From 9a8fd13bacaea14645f1cfd69e56b33f8ecbc1bd Mon Sep 17 00:00:00 2001 From: Tatiana Nesterenko Date: Thu, 15 Aug 2024 22:59:19 +0100 Subject: [PATCH 6/7] test: check both walletConnect and bearer auth in the new API requests Signed-off-by: Tatiana Nesterenko --- cmd/neofs-rest-gw/integration_test.go | 172 ++++++++++++++++---------- 1 file changed, 109 insertions(+), 63 deletions(-) diff --git a/cmd/neofs-rest-gw/integration_test.go b/cmd/neofs-rest-gw/integration_test.go index 9b8e511..1f42bc3 100644 --- a/cmd/neofs-rest-gw/integration_test.go +++ b/cmd/neofs-rest-gw/integration_test.go @@ -150,9 +150,13 @@ func runTests(ctx context.Context, t *testing.T, key *keys.PrivateKey, node stri t.Run("rest new upload object", func(t *testing.T) { restNewObjectUpload(ctx, t, clientPool, cnrID, signer) }) t.Run("rest new upload object with bearer in cookie", func(t *testing.T) { restNewObjectUploadCookie(ctx, t, clientPool, cnrID, signer) }) - t.Run("rest new head object", func(t *testing.T) { restNewObjectHead(ctx, t, clientPool, &owner, cnrID, signer) }) - t.Run("rest new head by attribute", func(t *testing.T) { restNewObjectHeadByAttribute(ctx, t, clientPool, &owner, cnrID, signer) }) - t.Run("rest new get by attribute", func(t *testing.T) { restNewObjectGetByAttribute(ctx, t, clientPool, &owner, cnrID, signer) }) + t.Run("rest new upload object with wallet connect", func(t *testing.T) { restNewObjectUploadWC(ctx, t, clientPool, cnrID, signer) }) + t.Run("rest new head object", func(t *testing.T) { restNewObjectHead(ctx, t, clientPool, &owner, cnrID, signer, false) }) + t.Run("rest new head object with wallet connect", func(t *testing.T) { restNewObjectHead(ctx, t, clientPool, &owner, cnrID, signer, true) }) + t.Run("rest new head by attribute", func(t *testing.T) { restNewObjectHeadByAttribute(ctx, t, clientPool, &owner, cnrID, signer, false) }) + t.Run("rest new head by attribute with wallet connect", func(t *testing.T) { restNewObjectHeadByAttribute(ctx, t, clientPool, &owner, cnrID, signer, true) }) + t.Run("rest new get by attribute", func(t *testing.T) { restNewObjectGetByAttribute(ctx, t, clientPool, &owner, cnrID, signer, false) }) + t.Run("rest new get by attribute with wallet connect", func(t *testing.T) { restNewObjectGetByAttribute(ctx, t, clientPool, &owner, cnrID, signer, true) }) } func createDockerContainer(ctx context.Context, t *testing.T, image, version string) testcontainers.Container { @@ -1871,12 +1875,15 @@ func restObjectUploadInt(ctx context.Context, t *testing.T, clientPool *pool.Poo } func restNewObjectUpload(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID cid.ID, signer user.Signer) { - restNewObjectUploadInt(ctx, t, clientPool, cnrID, signer, false) + restNewObjectUploadInt(ctx, t, clientPool, cnrID, signer, false, false) } func restNewObjectUploadCookie(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID cid.ID, signer user.Signer) { - restNewObjectUploadInt(ctx, t, clientPool, cnrID, signer, true) + restNewObjectUploadInt(ctx, t, clientPool, cnrID, signer, true, false) } -func restNewObjectUploadInt(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID cid.ID, signer user.Signer, cookie bool) { +func restNewObjectUploadWC(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID cid.ID, signer user.Signer) { + restNewObjectUploadInt(ctx, t, clientPool, cnrID, signer, false, true) +} +func restNewObjectUploadInt(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID cid.ID, signer user.Signer, cookie bool, walletConnect bool) { bt := apiserver.Bearer{ Object: []apiserver.Record{{ Operation: apiserver.OperationPUT, @@ -1897,15 +1904,15 @@ func restNewObjectUploadInt(ctx context.Context, t *testing.T, clientPool *pool. query := make(url.Values) query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect)) - // check that object bearer token is valid - request, err := http.NewRequest(http.MethodGet, testHost+"/v1/auth/bearer?"+query.Encode(), nil) - require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) resp := &apiserver.BinaryBearer{} - doRequest(t, httpClient, request, http.StatusOK, resp) - - actualTokenRaw, err := base64.StdEncoding.DecodeString(resp.Token) - require.NoError(t, err) + if !walletConnect { + request, err := http.NewRequest(http.MethodGet, testHost+"/v1/auth/bearer?"+query.Encode(), nil) + require.NoError(t, err) + prepareCommonHeaders(request.Header, bearerToken) + doRequest(t, httpClient, request, http.StatusOK, resp) + _, err = base64.StdEncoding.DecodeString(resp.Token) + require.NoError(t, err) + } content := "content of file" attributes := map[string]string{ @@ -1917,25 +1924,30 @@ func restNewObjectUploadInt(ctx context.Context, t *testing.T, clientPool *pool. attributesJSON, err := json.Marshal(attributes) require.NoError(t, err) - queryNew := make(url.Values) - queryNew.Add(fullBearerQuery, "true") - + if !walletConnect { + // Change the query, we only need the `fullBearer` parameter here. + query = make(url.Values) + query.Add(fullBearerQuery, "true") + } body := bytes.NewBufferString(content) - request, err = http.NewRequest(http.MethodPost, testHost+"/v1/objects/"+cnrID.String()+"?"+queryNew.Encode(), body) + request, err := http.NewRequest(http.MethodPost, testHost+"/v1/objects/"+cnrID.String()+"?"+query.Encode(), body) require.NoError(t, err) - request.Header.Set("Content-Type", "text/plain") - request.Header.Set("X-Attributes", string(attributesJSON)) - if cookie { - request.Header.Add("Cookie", "Bearer="+base64.StdEncoding.EncodeToString(actualTokenRaw)+";") + if !walletConnect { + request.Header.Set("Content-Type", "text/plain") + if cookie { + request.Header.Add("Cookie", "Bearer="+resp.Token+";") + } else { + request.Header.Add("Authorization", "Bearer "+resp.Token) + } } else { - request.Header.Add("Authorization", "Bearer "+base64.StdEncoding.EncodeToString(actualTokenRaw)) + prepareCommonHeaders(request.Header, bearerToken) } + + request.Header.Set("X-Attributes", string(attributesJSON)) addr := &apiserver.AddressForUpload{} doRequest(t, httpClient, request, http.StatusOK, addr) - request.Header.Set("Content-Type", "text/plain") - var CID cid.ID err = CID.DecodeString(addr.ContainerId) require.NoError(t, err) @@ -1958,7 +1970,7 @@ func restNewObjectUploadInt(ctx context.Context, t *testing.T, clientPool *pool. } } -func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID, signer user.Signer) { +func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID, signer user.Signer, walletConnect bool) { bearer := apiserver.Bearer{ Object: []apiserver.Record{ { @@ -1990,11 +2002,13 @@ func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID query := make(url.Values) query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect)) - request, err := http.NewRequest(http.MethodGet, testHost+"/v1/auth/bearer?"+query.Encode(), nil) - require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) resp := &apiserver.BinaryBearer{} - doRequest(t, httpClient, request, http.StatusOK, resp) + if !walletConnect { + request, err := http.NewRequest(http.MethodGet, testHost+"/v1/auth/bearer?"+query.Encode(), nil) + require.NoError(t, err) + prepareCommonHeaders(request.Header, bearerToken) + doRequest(t, httpClient, request, http.StatusOK, resp) + } var ( content = []byte("some content") @@ -2009,8 +2023,11 @@ func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID } ) - queryNew := make(url.Values) - queryNew.Add(fullBearerQuery, "true") + if !walletConnect { + // Change the query, we only need the `fullBearer` parameter here. + query = make(url.Values) + query.Add(fullBearerQuery, "true") + } t.Run("head", func(t *testing.T) { objID := createObject(ctx, t, p, ownerID, cnrID, attributes, content, signer) @@ -2019,10 +2036,14 @@ func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID createTS, err := strconv.ParseInt(attrTS, 10, 64) require.NoError(t, err) - request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_id/"+objID.EncodeToString()+"?"+queryNew.Encode(), nil) + request, err := http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_id/"+objID.EncodeToString()+"?"+query.Encode(), nil) require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) - request.Header.Set("Authorization", "Bearer "+resp.Token) + + if !walletConnect { + request.Header.Set("Authorization", "Bearer "+resp.Token) + } else { + prepareCommonHeaders(request.Header, bearerToken) + } headers, _ := doRequest(t, httpClient, request, http.StatusOK, nil) require.NotEmpty(t, headers) @@ -2072,10 +2093,14 @@ func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID createTS, err := strconv.ParseInt(attrTS, 10, 64) require.NoError(t, err) - request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_id/"+objID.EncodeToString()+"?"+queryNew.Encode(), nil) + request, err := http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_id/"+objID.EncodeToString()+"?"+query.Encode(), nil) require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) - request.Header.Set("Authorization", "Bearer "+resp.Token) + + if !walletConnect { + request.Header.Set("Authorization", "Bearer "+resp.Token) + } else { + prepareCommonHeaders(request.Header, bearerToken) + } headers, _ := doRequest(t, httpClient, request, http.StatusOK, nil) require.NotEmpty(t, headers) @@ -2116,7 +2141,7 @@ func restNewObjectHead(ctx context.Context, t *testing.T, p *pool.Pool, ownerID }) } -func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID, signer user.Signer) { +func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID, signer user.Signer, walletConnect bool) { bearer := apiserver.Bearer{ Object: []apiserver.Record{ { @@ -2157,15 +2182,17 @@ func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Poo query := make(url.Values) query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect)) - request, err := http.NewRequest(http.MethodGet, testHost+"/v1/auth/bearer?"+query.Encode(), nil) - require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) resp := &apiserver.BinaryBearer{} - doRequest(t, httpClient, request, http.StatusOK, resp) + if !walletConnect { + request, err := http.NewRequest(http.MethodGet, testHost+"/v1/auth/bearer?"+query.Encode(), nil) + require.NoError(t, err) + prepareCommonHeaders(request.Header, bearerToken) + doRequest(t, httpClient, request, http.StatusOK, resp) + } var ( content = []byte("some content") - fileNameAttr = "new-head-obj-by-attr-name-echo" + fileNameAttr = "new-head-obj-by-attr-name-" + strconv.FormatBool(walletConnect) attrKey = "soME-attribute" attrValue = "user value" attributes = map[string]string{ @@ -2175,8 +2202,10 @@ func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Poo } ) - queryNew := make(url.Values) - queryNew.Add(fullBearerQuery, "true") + if !walletConnect { + query = make(url.Values) + query.Add(fullBearerQuery, "true") + } t.Run("head", func(t *testing.T) { objID := createObject(ctx, t, p, ownerID, cnrID, attributes, content, signer) @@ -2185,10 +2214,14 @@ func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Poo createTS, err := strconv.ParseInt(attrTS, 10, 64) require.NoError(t, err) - request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+fileNameAttr+"?"+queryNew.Encode(), nil) + request, err := http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+fileNameAttr+"?"+query.Encode(), nil) require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) - request.Header.Set("Authorization", "Bearer "+resp.Token) + + if !walletConnect { + request.Header.Set("Authorization", "Bearer "+resp.Token) + } else { + prepareCommonHeaders(request.Header, bearerToken) + } headers, _ := doRequest(t, httpClient, request, http.StatusOK, nil) require.NotEmpty(t, headers) @@ -2238,10 +2271,14 @@ func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Poo createTS, err := strconv.ParseInt(attrTS, 10, 64) require.NoError(t, err) - request, err = http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+multiSegmentName+"?"+queryNew.Encode(), nil) + request, err := http.NewRequest(http.MethodHead, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+multiSegmentName+"?"+query.Encode(), nil) require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) - request.Header.Set("Authorization", "Bearer "+resp.Token) + + if !walletConnect { + request.Header.Set("Authorization", "Bearer "+resp.Token) + } else { + prepareCommonHeaders(request.Header, bearerToken) + } headers, _ := doRequest(t, httpClient, request, http.StatusOK, nil) require.NotEmpty(t, headers) @@ -2282,7 +2319,7 @@ func restNewObjectHeadByAttribute(ctx context.Context, t *testing.T, p *pool.Poo }) } -func restNewObjectGetByAttribute(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID, signer user.Signer) { +func restNewObjectGetByAttribute(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID, signer user.Signer, walletConnect bool) { bearer := apiserver.Bearer{ Object: []apiserver.Record{ { @@ -2314,15 +2351,17 @@ func restNewObjectGetByAttribute(ctx context.Context, t *testing.T, p *pool.Pool query := make(url.Values) query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect)) - request, err := http.NewRequest(http.MethodGet, testHost+"/v1/auth/bearer?"+query.Encode(), nil) - require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) resp := &apiserver.BinaryBearer{} - doRequest(t, httpClient, request, http.StatusOK, resp) + if !walletConnect { + request, err := http.NewRequest(http.MethodGet, testHost+"/v1/auth/bearer?"+query.Encode(), nil) + require.NoError(t, err) + prepareCommonHeaders(request.Header, bearerToken) + doRequest(t, httpClient, request, http.StatusOK, resp) + } var ( content = []byte("some content") - fileNameAttr = "new-get-obj-by-attr-name-echo" + fileNameAttr = "new-get-obj-by-attr-name-" + strconv.FormatBool(walletConnect) createTS = time.Now().Unix() attrKey = "user-attribute" attrValue = "user value" @@ -2336,13 +2375,20 @@ func restNewObjectGetByAttribute(ctx context.Context, t *testing.T, p *pool.Pool t.Run("get", func(t *testing.T) { objID := createObject(ctx, t, p, ownerID, cnrID, attributes, content, signer) - queryNew := make(url.Values) - queryNew.Add(fullBearerQuery, "true") + if !walletConnect { + // Change the query, we only need the `fullBearer` parameter here. + query = make(url.Values) + query.Add(fullBearerQuery, "true") + } - request, err = http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+fileNameAttr+"?"+queryNew.Encode(), nil) + request, err := http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/by_attribute/"+object.AttributeFileName+"/"+fileNameAttr+"?"+query.Encode(), nil) require.NoError(t, err) - prepareCommonHeaders(request.Header, bearerToken) - request.Header.Set("Authorization", "Bearer "+resp.Token) + + if !walletConnect { + request.Header.Set("Authorization", "Bearer "+resp.Token) + } else { + prepareCommonHeaders(request.Header, bearerToken) + } headers, rawPayload := doRequest(t, httpClient, request, http.StatusOK, nil) require.NotEmpty(t, headers) From a4827bb427b957ba26192a3417de13e5351a03cb Mon Sep 17 00:00:00 2001 From: Tatiana Nesterenko Date: Thu, 15 Aug 2024 23:21:16 +0100 Subject: [PATCH 7/7] test: update neofs-aio version to 0.42.1 Signed-off-by: Tatiana Nesterenko --- cmd/neofs-rest-gw/integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/neofs-rest-gw/integration_test.go b/cmd/neofs-rest-gw/integration_test.go index 1f42bc3..391c87b 100644 --- a/cmd/neofs-rest-gw/integration_test.go +++ b/cmd/neofs-rest-gw/integration_test.go @@ -89,7 +89,7 @@ func runLocalTests(ctx context.Context, t *testing.T, key *keys.PrivateKey) { func runTestInContainer(rootCtx context.Context, t *testing.T, key *keys.PrivateKey) { versions := []dockerImage{ - {image: "nspccdev/neofs-aio", version: "0.41.0"}, + {image: "nspccdev/neofs-aio", version: "0.42.1"}, } for _, version := range versions {