Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: validation checks bzz and stewardship #3170

Merged
merged 11 commits into from
Aug 23, 2022
25 changes: 6 additions & 19 deletions openapi/Swarm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -276,25 +276,6 @@ paths:
description: Default response

"/bzz/{reference}":
patch:
summary: "Reupload a root hash to the network; deprecated: use /stewardship/{reference} instead"
deprecated: true
tags:
- BZZ
parameters:
- in: path
name: reference
schema:
$ref: "SwarmCommon.yaml#/components/schemas/SwarmReference"
required: true
description: "Root hash of content (can be of any type: collection, file, chunk)"
responses:
"200":
description: Ok
"500":
$ref: "SwarmCommon.yaml#/components/responses/500"
default:
description: Default response
get:
summary: "Get file or index document from a collection of files"
tags:
Expand Down Expand Up @@ -843,10 +824,16 @@ paths:
responses:
"200":
description: Ok
"400":
$ref: "SwarmCommon.yaml#/components/responses/400"
"404":
$ref: "SwarmCommon.yaml#/components/responses/404"
"422":
$ref: "SwarmCommon.yaml#/components/responses/422"
"500":
$ref: "SwarmCommon.yaml#/components/responses/500"
"503":
$ref: "SwarmCommon.yaml#/components/responses/503"
default:
description: Default response

Expand Down
12 changes: 12 additions & 0 deletions openapi/SwarmCommon.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,12 @@ components:
application/problem+json:
schema:
$ref: "#/components/schemas/ProblemDetails"
"422":
description: Unprocessable Entity
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ProblemDetails"
"429":
description: Too many requests
content:
Expand All @@ -887,6 +893,12 @@ components:
application/problem+json:
schema:
$ref: "#/components/schemas/ProblemDetails"
"503":
description: Service Unavailable
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ProblemDetails"

"GatewayForbidden":
description: "Endpoint or header (pinning or encryption headers) forbidden in Gateway mode"
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ func (s *Service) resolveNameOrAddress(str string) (swarm.Address, error) {
return addr, nil
}

return swarm.ZeroAddress, fmt.Errorf("%w: %v", errInvalidNameOrAddress, err)
return swarm.ZeroAddress, fmt.Errorf("%v: %w", errInvalidNameOrAddress, err)
}

// requestModePut returns the desired storage.ModePut for this request based on the request headers.
Expand Down
6 changes: 1 addition & 5 deletions pkg/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ func TestParseName(t *testing.T) {
name: "not.good",
res: resolverMock.NewResolver(
resolverMock.WithResolveFunc(func(string) (swarm.Address, error) {
return swarm.ZeroAddress, errors.New("failed to resolve")
return swarm.ZeroAddress, api.ErrInvalidNameOrAddress
}),
),
wantErr: api.ErrInvalidNameOrAddress,
Expand Down Expand Up @@ -481,10 +481,6 @@ func TestOptions(t *testing.T) {
endpoint: "bzz",
expectedMethods: "POST",
},
{
endpoint: "bzz/0101011",
expectedMethods: "GET, PATCH",
},
{
endpoint: "chunks",
expectedMethods: "POST",
Expand Down
20 changes: 0 additions & 20 deletions pkg/api/bzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,23 +517,3 @@ func (s *Service) manifestFeed(
f := feeds.New(topic, common.BytesToAddress(owner))
return s.feedFactory.NewLookup(*t, f)
}

// bzzPatchHandler endpoint has been deprecated; use stewardship endpoint instead.
func (s *Service) bzzPatchHandler(w http.ResponseWriter, r *http.Request) {
nameOrHex := mux.Vars(r)["address"]
address, err := s.resolveNameOrAddress(nameOrHex)
if err != nil {
s.logger.Debug("bzz patch: parse address string failed", "string", nameOrHex, "error", err)
s.logger.Error(nil, "bzz patch: parse address string failed")
jsonhttp.NotFound(w, nil)
return
}
err = s.steward.Reupload(r.Context(), address)
if err != nil {
s.logger.Debug("bzz patch: reupload failed", "address", address, "error", err)
s.logger.Error(nil, "bzz patch: reupload failed")
jsonhttp.InternalServerError(w, "bzz patch: reupload failed")
return
}
jsonhttp.OK(w, nil)
}
26 changes: 0 additions & 26 deletions pkg/api/bzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
pinning "github.com/ethersphere/bee/pkg/pinning/mock"
mockpost "github.com/ethersphere/bee/pkg/postage/mock"
statestore "github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/steward/mock"
"github.com/ethersphere/bee/pkg/storage"
smock "github.com/ethersphere/bee/pkg/storage/mock"
"github.com/ethersphere/bee/pkg/swarm"
Expand Down Expand Up @@ -644,28 +643,3 @@ func TestFeedIndirection(t *testing.T) {
jsonhttptest.WithExpectedResponse(updateData),
)
}

func TestBzzReupload(t *testing.T) {
var (
logger = log.Noop
statestoreMock = statestore.NewStateStore()
stewardMock = &mock.Steward{}
storer = smock.NewStorer()
addr = swarm.NewAddress([]byte{31: 128})
)
client, _, _, _ := newTestServer(t, testServerOptions{
Storer: storer,
Tags: tags.NewTags(statestoreMock, logger),
Logger: logger,
Steward: stewardMock,
})
jsonhttptest.Request(t, client, http.MethodPatch, "/v1/bzz/"+addr.String(), http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
)
if !stewardMock.LastAddress().Equal(addr) {
t.Fatalf("got address %s want %s", stewardMock.LastAddress().String(), addr.String())
}
}
7 changes: 1 addition & 6 deletions pkg/api/cors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func TestCors(t *testing.T) {
expectedMethods: "POST",
}, {
endpoint: "bzz/0101011",
expectedMethods: "GET, PATCH",
expectedMethods: "GET",
},
{
endpoint: "chunks",
Expand Down Expand Up @@ -181,11 +181,6 @@ func TestCorsStatus(t *testing.T) {
notAllowedMethods: http.MethodDelete,
allowedMethods: "POST",
},
{
endpoint: "bzz/0101011",
notAllowedMethods: http.MethodDelete,
allowedMethods: "GET, PATCH",
},
{
endpoint: "chunks",
notAllowedMethods: http.MethodDelete,
Expand Down
4 changes: 0 additions & 4 deletions pkg/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,6 @@ func (s *Service) mountAPI() {
s.newTracingHandler("bzz-download"),
web.FinalHandlerFunc(s.bzzDownloadHandler),
),
"PATCH": web.ChainHandlers(
s.newTracingHandler("bzz-patch"),
web.FinalHandlerFunc(s.bzzPatchHandler),
),
})

handle("/pss/send/{topic}/{targets}", web.ChainHandlers(
Expand Down
25 changes: 22 additions & 3 deletions pkg/api/stewardship.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
package api

import (
"errors"
"net/http"

"github.com/ethersphere/bee/pkg/resolver"

"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/gorilla/mux"
)
Expand All @@ -16,10 +19,26 @@ import (
func (s *Service) stewardshipPutHandler(w http.ResponseWriter, r *http.Request) {
nameOrHex := mux.Vars(r)["address"]
address, err := s.resolveNameOrAddress(nameOrHex)
if err != nil {
switch {
case errors.Is(err, resolver.ErrParse), errors.Is(err, resolver.ErrInvalidContentHash):
s.logger.Debug("stewardship put: parse address string failed", "string", nameOrHex, "error", err)
s.logger.Error(nil, "stewardship put: parse address string failed")
jsonhttp.NotFound(w, nil)
s.logger.Error(nil, "stewardship put: invalid address")
jsonhttp.BadRequest(w, "invalid address")
return
case errors.Is(err, resolver.ErrNotFound):
s.logger.Debug("stewardship put: address not found", "string", nameOrHex, "error", err)
s.logger.Error(nil, "stewardship put: address not found")
jsonhttp.NotFound(w, "address not found")
return
case errors.Is(err, resolver.ErrServiceNotAvailable):
s.logger.Debug("stewardship put: service unavailable", "string", nameOrHex, "error", err)
s.logger.Error(nil, "stewardship put: service unavailable")
jsonhttp.InternalServerError(w, "stewardship put: resolver service unavailable")
return
case err != nil:
s.logger.Debug("stewardship put: resolve address or name string failed", "string", nameOrHex, "error", err)
s.logger.Error(nil, "stewardship put: resolve address or name string failed")
jsonhttp.InternalServerError(w, "stewardship put: resolve name or address")
return
}
err = s.steward.Reupload(r.Context(), address)
Expand Down
58 changes: 58 additions & 0 deletions pkg/api/stewardship_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/log"
"github.com/ethersphere/bee/pkg/resolver"
resolverMock "github.com/ethersphere/bee/pkg/resolver/mock"
statestore "github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/steward/mock"
smock "github.com/ethersphere/bee/pkg/storage/mock"
Expand Down Expand Up @@ -59,3 +61,59 @@ func TestStewardship(t *testing.T) {
)
})
}

func TestStewardshipInputValidations(t *testing.T) {
var (
logger = log.Noop
statestoreMock = statestore.NewStateStore()
stewardMock = &mock.Steward{}
storer = smock.NewStorer()
)
client, _, _, _ := newTestServer(t, testServerOptions{
Storer: storer,
Tags: tags.NewTags(statestoreMock, logger),
Logger: logger,
Steward: stewardMock,
Resolver: resolverMock.NewResolver(
resolverMock.WithResolveFunc(
func(string) (swarm.Address, error) {
return swarm.Address{}, resolver.ErrParse
},
),
),
})
for _, tt := range []struct {
name string
reference string
expectedStatus int
expectedMessage string
}{
{
name: "correct reference",
reference: "1e477b015af480e387fbf5edd90f1685a30c0e3ba88eeb3871b326b816a542da",
expectedStatus: http.StatusOK,
expectedMessage: http.StatusText(http.StatusOK),
},
{
name: "reference not found",
reference: "1e477b015af480e387fbf5edd90f1685a30c0e3ba88eeb3871b326b816a542d/",
expectedStatus: http.StatusNotFound,
expectedMessage: http.StatusText(http.StatusNotFound),
},
{
name: "incorrect reference",
reference: "xc0f6",
expectedStatus: http.StatusBadRequest,
expectedMessage: "invalid address",
},
} {
t.Run("input validation -"+tt.name, func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPut, "/v1/stewardship/"+tt.reference, tt.expectedStatus,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: tt.expectedMessage,
Code: tt.expectedStatus,
}),
)
})
}
}
14 changes: 10 additions & 4 deletions pkg/resolver/cidv1/cidv1.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package cidv1
import (
"fmt"

"github.com/ethersphere/bee/pkg/resolver"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
Expand All @@ -24,23 +25,28 @@ type Resolver struct{}
func (Resolver) Resolve(name string) (swarm.Address, error) {
id, err := cid.Parse(name)
if err != nil {
return swarm.ZeroAddress, fmt.Errorf("failed parsing CID %s err %w", name, err)
return swarm.ZeroAddress, fmt.Errorf("failed parsing CID %s err %v: %w", name, err, resolver.ErrParse)
}

switch id.Prefix().GetCodec() {
case SwarmNsCodec:
case SwarmManifestCodec:
case SwarmFeedCodec:
default:
return swarm.ZeroAddress, fmt.Errorf("unsupported codec for CID %d", id.Prefix().GetCodec())
return swarm.ZeroAddress, fmt.Errorf("unsupported codec for CID %d: %w", id.Prefix().GetCodec(), resolver.ErrParse)
}

dh, err := multihash.Decode(id.Hash())
if err != nil {
return swarm.ZeroAddress, fmt.Errorf("unable to decode hash %w", err)
return swarm.ZeroAddress, fmt.Errorf("unable to decode hash %v: %w", err, resolver.ErrInvalidContentHash)
}

return swarm.NewAddress(dh.Digest), nil
addr, err := swarm.NewAddress(dh.Digest), nil
if err != nil {
return swarm.ZeroAddress, fmt.Errorf("unable to parse digest hash %v: %w", err, resolver.ErrInvalidContentHash)
}

return addr, nil
}

func (Resolver) Close() error {
Expand Down
8 changes: 8 additions & 0 deletions pkg/resolver/cidv1/cidv1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
package cidv1_test

import (
"errors"
"testing"

"github.com/ethersphere/bee/pkg/resolver"
"github.com/ethersphere/bee/pkg/resolver/cidv1"
)

Expand Down Expand Up @@ -47,4 +49,10 @@ func TestCIDResolver(t *testing.T) {
t.Fatal("expected error")
}
})
t.Run("fail on invalid CID", func(t *testing.T) {
_, err := r.Resolve("bafybeiekk")
if !errors.Is(err, resolver.ErrParse) {
t.Fatal("expected error", resolver.ErrParse, "got", err)
}
})
}
Loading