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

ACT grantee management #37

Merged
merged 14 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ type Service struct {
feedFactory feeds.Factory
signer crypto.Signer
post postage.Service
dac dynamicaccess.Service
dac dynamicaccess.Controller
postageContract postagecontract.Interface
probe *Probe
metricsRegistry *prometheus.Registry
Expand Down Expand Up @@ -253,7 +253,7 @@ type ExtraOptions struct {
Pss pss.Interface
FeedFactory feeds.Factory
Post postage.Service
Dac dynamicaccess.Service
Dac dynamicaccess.Controller
PostageContract postagecontract.Interface
Staking staking.Contract
Steward steward.Interface
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ type testServerOptions struct {
PostageContract postagecontract.Interface
StakingContract staking.Contract
Post postage.Service
Dac dynamicaccess.Service
Dac dynamicaccess.Controller
Steward steward.Interface
WsHeaders http.Header
Authenticator auth.Authenticator
Expand Down
122 changes: 122 additions & 0 deletions pkg/api/dynamicaccess.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ package api
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"encoding/json"
"io"
"net/http"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/ethersphere/bee/v2/pkg/crypto"
"github.com/ethersphere/bee/v2/pkg/jsonhttp"
"github.com/ethersphere/bee/v2/pkg/log"
storer "github.com/ethersphere/bee/v2/pkg/storer"
Expand All @@ -28,6 +33,16 @@ func setAddressInContext(ctx context.Context, address swarm.Address) context.Con
return context.WithValue(ctx, addressKey{}, address)
}

type GranteesPatchRequest struct {
Addlist []string `json:"add"`
Revokelist []string `json:"revoke"`
}

type GranteesPatch struct {
Addlist []ecdsa.PublicKey
Revokelist []ecdsa.PublicKey
}

// actDecryptionHandler is a middleware that looks up and decrypts the given address,
// if the act headers are present
func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler {
Expand Down Expand Up @@ -113,3 +128,110 @@ func (s *Service) actEncryptionHandler(

return encryptedReference, nil
}

func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) {
logger := s.logger.WithName("acthandler").Build()
paths := struct {
GranteesAddress swarm.Address `map:"address,resolve" validate:"required"`
}{}
if response := s.mapStructure(r.Header, &paths); response != nil {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace r.Header with mux.Vars

response("invalid path params", logger, w)
return
}
grantees, err := s.dac.GetGrantees(r.Context(), paths.GranteesAddress)
if err != nil {
jsonhttp.NotFound(w, "grantee list not found")
return
}
granteeSlice := make([]string, len(grantees))
for i, grantee := range grantees {
granteeSlice[i] = hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(grantee))
}
jsonhttp.OK(w, granteeSlice)
}

func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) {
logger := s.logger.WithName("acthandler").Build()

if r.Body == http.NoBody {
logger.Error(nil, "request has no body")
jsonhttp.BadRequest(w, errInvalidRequest)
return
}

paths := struct {
GranteesAddress swarm.Address `map:"address,resolve" validate:"required"`
}{}
if response := s.mapStructure(r.Header, &paths); response != nil {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace r.Header with mux.Vars

response("invalid path params", logger, w)
return
}

headers := struct {
BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"`
Publisher *ecdsa.PublicKey `map:"Swarm-Act-Publisher" validate:"required"`
HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"`
}{}
if response := s.mapStructure(r.Header, &headers); response != nil {
response("invalid header params", logger, w)
return
}

body, err := io.ReadAll(r.Body)
if err != nil {
if jsonhttp.HandleBodyReadError(err, w) {
return
}
logger.Debug("read request body failed", "error", err)
logger.Error(nil, "read request body failed")
jsonhttp.InternalServerError(w, "cannot read request")
return
}

gpr := GranteesPatchRequest{}
if len(body) > 0 {
err = json.Unmarshal(body, &gpr)
if err != nil {
logger.Debug("unmarshal body failed", "error", err)
logger.Error(nil, "unmarshal body failed")
jsonhttp.InternalServerError(w, "error unmarshaling request body")
return
}
}

grantees := GranteesPatch{}
for _, g := range gpr.Addlist {
h, _ := hex.DecodeString(g)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add Errror handling

k, _ := btcec.ParsePubKey(h)
grantees.Addlist = append(grantees.Addlist, *k.ToECDSA())
}
for _, g := range gpr.Revokelist {
h, _ := hex.DecodeString(g)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add Errror handling

k, _ := btcec.ParsePubKey(h)
grantees.Revokelist = append(grantees.Revokelist, *k.ToECDSA())
}

tag, _ := s.getOrCreateSessionID(0)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe read headers.SwarmTag as tagid.


ctx := r.Context()
putter, _ := s.newStamperPutter(ctx, putterOptions{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add Errror handling

BatchID: headers.BatchID,
TagID: tag,
Pin: false,
Deferred: true,
})

granteeref := paths.GranteesAddress
granteeref, historyref, _ := s.dac.HandleGrantees(ctx, granteeref, *headers.HistoryAddress, headers.Publisher, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add Errror handling

putter.Done(granteeref)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add Errror handling

putter.Done(historyref)
jsonhttp.OK(w, nil)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shall be .Created(granteeresponse) with historyref and granteeref in it.

}

func convertToPointerSlice(slice []ecdsa.PublicKey) []*ecdsa.PublicKey {
pointerSlice := make([]*ecdsa.PublicKey, len(slice))
for i, key := range slice {
pointerSlice[i] = &key
}
return pointerSlice
}
75 changes: 75 additions & 0 deletions pkg/api/dynamicaccess_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -802,3 +802,78 @@ func TestDacPublisher(t *testing.T) {
)
})
}

func TestDacGrantees(t *testing.T) {
t.Parallel()
var (
spk, _ = hex.DecodeString("a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa")
pk, _ = crypto.DecodeSecp256k1PrivateKey(spk)
publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey)
publisher = hex.EncodeToString(publicKeyBytes)
storerMock = mockstorer.New()
h, fixtureHref = prepareHistoryFixture(storerMock)
logger = log.Noop
addr = swarm.RandAddress(t)
client, _, _, _ = newTestServer(t, testServerOptions{
Storer: storerMock,
Logger: logger,
Post: mockpost.New(mockpost.WithAcceptAll()),
PublicKey: pk.PublicKey,
Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())),
})
)
t.Run("get-grantees", func(t *testing.T) {
expected := []string{
"03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2",
"03c712a7e29bc792ac8d8ae49793d28d5bda27ed70f0d90697b2fb456c0a168bd2",
"032541acf966823bae26c2c16a7102e728ade3e2e29c11a8a17b29d8eb2bd19302",
}
jsonhttptest.Request(t, client, http.MethodGet, "/grantee/"+addr.String(), http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(expected),
)
})

t.Run("get-grantees-missing-address", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodGet, "/grantee/123", http.StatusBadRequest,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "Unauthorized",
Code: http.StatusBadRequest,
}),
)
})
t.Run("get-grantees-wrong-address", func(t *testing.T) {
})
t.Run("add-revoke-grantees", func(t *testing.T) {
})
t.Run("add-revoke-grantees-empty-body", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusBadRequest,
jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr),
jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher),
jsonhttptest.WithRequestBody(bytes.NewReader(nil)),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "could not validate request",
Code: http.StatusBadRequest,
}),
)
})
t.Run("add-grantee-with-history", func(t *testing.T) {
})
t.Run("add-grantee-without-history", func(t *testing.T) {
body := api.GranteesPatchRequest{
Addlist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"},
}
jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusOK,
jsonhttptest.WithJSONRequestBody(body),
)

expected := []string{
"03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2",
"03c712a7e29bc792ac8d8ae49793d28d5bda27ed70f0d90697b2fb456c0a168bd2",
"032541acf966823bae26c2c16a7102e728ade3e2e29c11a8a17b29d8eb2bd19302",
"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4",
}
jsonhttptest.Request(t, client, http.MethodGet, "/grantee/"+addr.String(), http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(expected),
)
})
}
9 changes: 9 additions & 0 deletions pkg/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,15 @@ func (s *Service) mountAPI() {
),
})

handle("/grantee/{address}", jsonhttp.MethodHandler{
"GET": web.ChainHandlers(
web.FinalHandlerFunc(s.actListGranteesHandler),
),
"PATCH": web.ChainHandlers(
web.FinalHandlerFunc(s.actGrantRevokeHandler),
),
})

handle("/bzz/{address}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
u := r.URL
u.Path += "/"
Expand Down
Loading