From 0e4fa620ce9c7435888b65a8618a165a4ae9a26b Mon Sep 17 00:00:00 2001 From: Kexort Date: Mon, 22 Apr 2024 08:18:24 +0200 Subject: [PATCH 01/14] implement grantee management --- pkg/api/api.go | 4 +- pkg/api/api_test.go | 2 +- pkg/api/dynamicaccess.go | 122 +++++++++++++++++++++ pkg/api/dynamicaccess_test.go | 75 +++++++++++++ pkg/api/router.go | 9 ++ pkg/dynamicaccess/controller.go | 158 +++++++++++++-------------- pkg/dynamicaccess/controller_test.go | 145 ++++++++++++++++++------ pkg/dynamicaccess/grantee.go | 3 +- pkg/dynamicaccess/mock/service.go | 40 ++++--- pkg/dynamicaccess/service.go | 39 ------- pkg/node/devnode.go | 6 +- pkg/node/node.go | 6 +- 12 files changed, 429 insertions(+), 180 deletions(-) delete mode 100644 pkg/dynamicaccess/service.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 00373c28186..ec5f4a508ad 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -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 @@ -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 diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index 00dd73fdd01..2c93a20353b 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -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 diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index c7c1279609e..ed68d4cbb5c 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -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" @@ -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 { @@ -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 { + 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 { + 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) + k, _ := btcec.ParsePubKey(h) + grantees.Addlist = append(grantees.Addlist, *k.ToECDSA()) + } + for _, g := range gpr.Revokelist { + h, _ := hex.DecodeString(g) + k, _ := btcec.ParsePubKey(h) + grantees.Revokelist = append(grantees.Revokelist, *k.ToECDSA()) + } + + tag, _ := s.getOrCreateSessionID(0) + + ctx := r.Context() + putter, _ := s.newStamperPutter(ctx, putterOptions{ + 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)) + putter.Done(granteeref) + putter.Done(historyref) + jsonhttp.OK(w, nil) +} + +func convertToPointerSlice(slice []ecdsa.PublicKey) []*ecdsa.PublicKey { + pointerSlice := make([]*ecdsa.PublicKey, len(slice)) + for i, key := range slice { + pointerSlice[i] = &key + } + return pointerSlice +} diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index 480714444e0..a9dc706c2b7 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -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), + ) + }) +} diff --git a/pkg/api/router.go b/pkg/api/router.go index 1b1e1c877cb..1a2040a2639 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -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 += "/" diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index fdef1c4971a..f6538af59c4 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -3,6 +3,7 @@ package dynamicaccess import ( "context" "crypto/ecdsa" + "io" "time" "github.com/ethersphere/bee/v2/pkg/file/loadsave" @@ -10,26 +11,16 @@ import ( "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/kvs" - kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" "github.com/ethersphere/bee/v2/pkg/storage" "github.com/ethersphere/bee/v2/pkg/swarm" ) +const granteeListEncrypt = true + type GranteeManager interface { - //PUT /grantees/{grantee} - //body: {publisher?, grantee root hash ,grantee} - Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error - //DELETE /grantees/{grantee} - //body: {publisher?, grantee root hash , grantee} - Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error - //[ ] - //POST /grantees - //body: {publisher, historyRootHash} - Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) - - //Post /grantees + //PATCH /grantees //{publisher, addList, removeList} - HandleGrantees(ctx context.Context, rootHash swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error + HandleGrantees(ctx context.Context, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) //GET /grantees/{history root hash} GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) @@ -43,15 +34,13 @@ type Controller interface { // TODO: history encryption // UploadHandler encrypts the reference and stores it in the history as the latest update. UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + io.Closer } type controller struct { accessLogic ActLogic - granteeList GranteeList - //[ ]: do we need to protect this with a mutex? - revokeFlag []swarm.Address - getter storage.Getter - putter storage.Putter + getter storage.Getter + putter storage.Putter } var _ Controller = (*controller)(nil) @@ -90,8 +79,8 @@ func (c *controller) UploadHandler( ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) historyRef := historyRootHash var ( - storage kvs.KeyValueStore - storageRef swarm.Address + storage kvs.KeyValueStore + actRef swarm.Address ) now := time.Now().Unix() if historyRef.IsZero() { @@ -107,12 +96,12 @@ func (c *controller) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - storageRef, err = storage.Save(ctx) + actRef, err = storage.Save(ctx) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } // TODO: pass granteelist ref as mtdt - err = history.Add(ctx, storageRef, &now, nil) + err = history.Add(ctx, actRef, &now, nil) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -127,101 +116,105 @@ func (c *controller) UploadHandler( } // TODO: hanlde granteelist ref in mtdt entry, err := history.Lookup(ctx, now) - storageRef = entry.Reference() + actRef = entry.Reference() if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - storage, err = kvs.NewReference(ls, storageRef) + storage, err = kvs.NewReference(ls, actRef) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } encryptedRef, err := c.accessLogic.EncryptRef(ctx, storage, publisher, refrefence) - return storageRef, historyRef, encryptedRef, err + return actRef, historyRef, encryptedRef, err } func NewController(ctx context.Context, accessLogic ActLogic, getter storage.Getter, putter storage.Putter) Controller { return &controller{ - granteeList: nil, accessLogic: accessLogic, getter: getter, putter: putter, } } -func (c *controller) Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { - return c.granteeList.Add([]*ecdsa.PublicKey{grantee}) -} +func (c *controller) HandleGrantees(ctx context.Context, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { + var ( + err error + h History + act kvs.KeyValueStore + ls = loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) + gls = loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, granteeListEncrypt, redundancy.NONE)) + ) + if !historyref.IsZero() { + h, err = NewHistoryReference(ls, historyref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + entry, err := h.Lookup(ctx, time.Now().Unix()) -func (c *controller) Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { - if !c.isRevokeFlagged(granteesAddress) { - c.setRevokeFlag(granteesAddress, true) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + actref := entry.Reference() + act, err = kvs.NewReference(ls, actref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + } else { + h, err = NewHistory(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + act, err = kvs.New(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } } - return c.granteeList.Remove([]*ecdsa.PublicKey{grantee}) -} -func (c *controller) Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { - var act kvs.KeyValueStore - if c.isRevokeFlagged(granteesAddress) { - act = kvsmock.New() - c.accessLogic.AddPublisher(ctx, act, publisher) + var gl GranteeList + if granteeref.IsZero() { + gl = NewGranteeList(gls) } else { - act = kvsmock.NewReference(actRootHash) + gl = NewGranteeListReference(gls, granteeref) } + gl.Add(addList) + gl.Remove(removeList) - grantees := c.granteeList.Get() - for _, grantee := range grantees { - c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + granteesToAdd := addList + + // generate new access key and new act + if len(removeList) != 0 || granteeref.IsZero() { + c.accessLogic.AddPublisher(ctx, act, publisher) + granteesToAdd = gl.Get() } - granteeref, err := c.granteeList.Save(ctx) - if err != nil { - return swarm.EmptyAddress, swarm.EmptyAddress, err + for _, grantee := range granteesToAdd { + c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) } actref, err := act.Save(ctx) if err != nil { - return swarm.EmptyAddress, swarm.EmptyAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, err } - c.setRevokeFlag(granteesAddress, false) - return granteeref, actref, err -} - -func (c *controller) HandleGrantees(ctx context.Context, granteesAddress swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error { - act := kvsmock.New() - - c.accessLogic.AddPublisher(ctx, act, publisher) - for _, grantee := range addList { - c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + h.Add(ctx, actref, nil, nil) + href, err := h.Store(ctx) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err } - return nil -} -func (c *controller) GetGrantees(ctx context.Context, granteeRootHash swarm.Address) ([]*ecdsa.PublicKey, error) { - return c.granteeList.Get(), nil -} - -func (c *controller) isRevokeFlagged(granteeRootHash swarm.Address) bool { - for _, revoke := range c.revokeFlag { - if revoke.Equal(granteeRootHash) { - return true - } + glref, err := gl.Save(ctx) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err } - return false + return glref, href, nil } -func (c *controller) setRevokeFlag(granteeRootHash swarm.Address, set bool) { - if set { - c.revokeFlag = append(c.revokeFlag, granteeRootHash) - } else { - for i, revoke := range c.revokeFlag { - if revoke.Equal(granteeRootHash) { - c.revokeFlag = append(c.revokeFlag[:i], c.revokeFlag[i+1:]...) - } - } - } +func (c *controller) GetGrantees(ctx context.Context, granteeRef swarm.Address) ([]*ecdsa.PublicKey, error) { + ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, granteeListEncrypt, redundancy.NONE)) + gl := NewGranteeListReference(ls, granteeRef) + return gl.Get(), nil } func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { @@ -229,3 +222,8 @@ func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) } } + +// TODO: what to do in close ? +func (s *controller) Close() error { + return nil +} diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index a48d426466e..55131d3793b 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -3,21 +3,16 @@ package dynamicaccess_test import ( "context" "crypto/ecdsa" - "encoding/hex" "testing" "time" "github.com/ethersphere/bee/v2/pkg/dynamicaccess" - "github.com/ethersphere/bee/v2/pkg/encryption" "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/kvs" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" - "golang.org/x/crypto/sha3" ) -var hashFunc = sha3.NewLegacyKeccak256 - func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) { h, err := dynamicaccess.NewHistory(ls) if err != nil { @@ -30,12 +25,12 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess. al.AddPublisher(ctx, kvs0, publisher) kvs0Ref, _ := kvs0.Save(ctx) kvs1, _ := kvs.New(ls) - al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey, nil) al.AddPublisher(ctx, kvs1, publisher) + al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey, nil) kvs1Ref, _ := kvs1.Save(ctx) kvs2, _ := kvs.New(ls) - al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey, nil) al.AddPublisher(ctx, kvs2, publisher) + al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey, nil) kvs2Ref, _ := kvs2.Save(ctx) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() @@ -47,55 +42,139 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess. return h.Store(ctx) } -// TODO: separate up down test with fixture, now these just check if the flow works at all -func TestController_NewUploadDownload(t *testing.T) { +func TestController_NewUpload(t *testing.T) { ctx := context.Background() - publisher := getPrivKey(1) + publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) ref := swarm.RandAddress(t) - _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, swarm.ZeroAddress) - assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, encryptedRef, &publisher.PublicKey, hRef, time.Now().Unix()) + _, hRef, encRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, swarm.ZeroAddress) + + ls := createLs() + h, err := dynamicaccess.NewHistoryReference(ls, hRef) + entry, err := h.Lookup(ctx, time.Now().Unix()) + actRef := entry.Reference() + act, err := kvs.NewReference(ls, actRef) + expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + assert.NoError(t, err) - assert.Equal(t, ref, dref) + assert.Equal(t, encRef, expRef) + assert.NotEqual(t, hRef, swarm.ZeroAddress) } -func TestController_ExistingUploadDownload(t *testing.T) { - ls := createLs() +func TestController_PublisherDownload(t *testing.T) { ctx := context.Background() publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + ls := createLs() ref := swarm.RandAddress(t) - hRef, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) - assert.NoError(t, err) - _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, hRef) + href, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) + h, err := dynamicaccess.NewHistoryReference(ls, href) + entry, err := h.Lookup(ctx, time.Now().Unix()) + actRef := entry.Reference() + act, err := kvs.NewReference(ls, actRef) + encRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, encryptedRef, &publisher.PublicKey, hRef, time.Now().Unix()) + dref, err := c.DownloadHandler(ctx, encRef, &publisher.PublicKey, href, time.Now().Unix()) assert.NoError(t, err) assert.Equal(t, ref, dref) } -func TestControllerGrant(t *testing.T) { -} +func TestController_GranteeDownload(t *testing.T) { + ctx := context.Background() + publisher := getPrivKey(0) + grantee := getPrivKey(2) + publisherDH := dynamicaccess.NewDefaultSession(publisher) + publisherAL := dynamicaccess.NewLogic(publisherDH) -func TestControllerRevoke(t *testing.T) { + diffieHellman := dynamicaccess.NewDefaultSession(grantee) + al := dynamicaccess.NewLogic(diffieHellman) + ls := createLs() + c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + ref := swarm.RandAddress(t) + href, err := getHistoryFixture(ctx, ls, publisherAL, &publisher.PublicKey) + h, err := dynamicaccess.NewHistoryReference(ls, href) + ts := time.Date(2001, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + entry, err := h.Lookup(ctx, ts) + actRef := entry.Reference() + act, err := kvs.NewReference(ls, actRef) + encRef, err := publisherAL.EncryptRef(ctx, act, &publisher.PublicKey, ref) + assert.NoError(t, err) + dref, err := c.DownloadHandler(ctx, encRef, &publisher.PublicKey, href, ts) + assert.NoError(t, err) + assert.Equal(t, ref, dref) } -func TestControllerCommit(t *testing.T) { +func TestController_HandleGrantees(t *testing.T) { + ctx := context.Background() + publisher := getPrivKey(1) + diffieHellman := dynamicaccess.NewDefaultSession(publisher) + al := dynamicaccess.NewLogic(diffieHellman) + ls := createLs() + c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + href, _ := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) -} + grantee1 := getPrivKey(0) + grantee := getPrivKey(2) -func prepareEncryptedChunkReference(ak []byte) (swarm.Address, swarm.Address) { - addr, _ := hex.DecodeString("f7b1a45b70ee91d3dbfd98a2a692387f24db7279a9c96c447409e9205cf265baef29bf6aa294264762e33f6a18318562c86383dd8bfea2cec14fae08a8039bf3") - e1 := encryption.New(ak, 0, uint32(0), hashFunc) - ech, err := e1.Encrypt(addr) - if err != nil { - return swarm.EmptyAddress, swarm.EmptyAddress - } - return swarm.NewAddress(ech), swarm.NewAddress(addr) + t.Run("add to new list", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + granteeRef, _, err := c.HandleGrantees(ctx, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + + gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 1) + }) + t.Run("add to existing list", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + granteeRef, _, err := c.HandleGrantees(ctx, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + + gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 1) + + addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} + granteeRef, _, err = c.HandleGrantees(ctx, granteeRef, href, &publisher.PublicKey, addList, nil) + gl = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + assert.NoError(t, err) + assert.Len(t, gl.Get(), 2) + }) + t.Run("add and revoke", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + revokeList := []*ecdsa.PublicKey{&grantee1.PublicKey} + gl := dynamicaccess.NewGranteeList(createLs()) + gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) + granteeRef, err := gl.Save(ctx) + + granteeRef, _, err = c.HandleGrantees(ctx, granteeRef, href, &publisher.PublicKey, addList, revokeList) + gl = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 2) + }) + + t.Run("add twice", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} + granteeRef, _, err := c.HandleGrantees(ctx, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, err = c.HandleGrantees(ctx, granteeRef, href, &publisher.PublicKey, addList, nil) + gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 1) + }) + t.Run("revoke non-existing", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + granteeRef, _, err := c.HandleGrantees(ctx, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 1) + }) } diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index 6724b718e4f..514dd2a550e 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -6,6 +6,7 @@ import ( "crypto/elliptic" "fmt" + "github.com/btcsuite/btcd/btcec/v2" "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/swarm" ) @@ -61,7 +62,7 @@ func (g *GranteeListStruct) deserialize(data []byte) []*ecdsa.PublicKey { } func (g *GranteeListStruct) deserializeBytes(data []byte) *ecdsa.PublicKey { - curve := elliptic.P256() + curve := btcec.S256() x, y := elliptic.Unmarshal(curve, data) return &ecdsa.PublicKey{Curve: curve, X: x, Y: y} } diff --git a/pkg/dynamicaccess/mock/service.go b/pkg/dynamicaccess/mock/service.go index 6f4d5f09e72..485de90f6a9 100644 --- a/pkg/dynamicaccess/mock/service.go +++ b/pkg/dynamicaccess/mock/service.go @@ -44,7 +44,7 @@ type Option interface { func (f optionFunc) apply(r *mockDacService) { f(r) } // New creates a new mock dynamicaccess service. -func New(o ...Option) dynamicaccess.Service { +func New(o ...Option) dynamicaccess.Controller { storer := mockstorer.New() m := &mockDacService{ historyMap: make(map[string]dynamicaccess.History), @@ -141,20 +141,32 @@ func (m *mockDacService) Close() error { return nil } -func (m *mockDacService) Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { - return nil -} -func (m *mockDacService) Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { - return nil -} -func (m *mockDacService) Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { - return swarm.ZeroAddress, swarm.ZeroAddress, nil -} -func (m *mockDacService) HandleGrantees(ctx context.Context, rootHash swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error { - return nil +func (m *mockDacService) HandleGrantees(ctx context.Context, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { + historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") + glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") + return glRef, historyRef, nil } -func (m *mockDacService) GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) { - return nil, nil +func (m *mockDacService) GetGrantees(ctx context.Context, granteeref swarm.Address) ([]*ecdsa.PublicKey, error) { + keys := []string{ + "a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa", + "b786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfb", + "c786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfc", + } + var pubkeys []*ecdsa.PublicKey + for i := range keys { + data, err := hex.DecodeString(keys[i]) + if err != nil { + panic(err) + } + + privKey, err := crypto.DecodeSecp256k1PrivateKey(data) + pubKey := privKey.PublicKey + if err != nil { + panic(err) + } + pubkeys = append(pubkeys, &pubKey) + } + return pubkeys, nil } func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { diff --git a/pkg/dynamicaccess/service.go b/pkg/dynamicaccess/service.go deleted file mode 100644 index b9cedbcf82b..00000000000 --- a/pkg/dynamicaccess/service.go +++ /dev/null @@ -1,39 +0,0 @@ -package dynamicaccess - -import ( - "context" - "crypto/ecdsa" - "io" - - "github.com/ethersphere/bee/v2/pkg/swarm" -) - -type Service interface { - DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) - UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) - io.Closer -} - -// TODO: is service needed at all? -> it is just a wrapper around controller -type service struct { - controller Controller -} - -func (s *service) DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { - return s.controller.DownloadHandler(ctx, encryptedRef, publisher, historyRootHash, timestamp) -} - -func (s *service) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { - return s.controller.UploadHandler(ctx, reference, publisher, historyRootHash) -} - -// TODO: what to do in close ? -func (s *service) Close() error { - return nil -} - -func NewService(controller Controller) (Service, error) { - return &service{ - controller: controller, - }, nil -} diff --git a/pkg/node/devnode.go b/pkg/node/devnode.go index e838f93a287..4e96a301769 100644 --- a/pkg/node/devnode.go +++ b/pkg/node/devnode.go @@ -238,11 +238,7 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { session := dynamicaccess.NewDefaultSession(mockKey) actLogic := dynamicaccess.NewLogic(session) - ctrl := dynamicaccess.NewController(context.Background(), actLogic, localStore.ChunkStore(), localStore.Cache()) - dac, err := dynamicaccess.NewService(ctrl) - if err != nil { - return nil, fmt.Errorf("dac service: %w", err) - } + dac := dynamicaccess.NewController(context.Background(), actLogic, localStore.ChunkStore(), localStore.Cache()) b.dacCloser = dac pssService := pss.New(mockKey, logger) diff --git a/pkg/node/node.go b/pkg/node/node.go index 786c9abe864..f16f2579e85 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -776,11 +776,7 @@ func NewBee( evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) } actLogic := dynamicaccess.NewLogic(session) - ctrl := dynamicaccess.NewController(ctx, actLogic, localStore.ChunkStore(), localStore.Cache()) - dac, err := dynamicaccess.NewService(ctrl) - if err != nil { - return nil, fmt.Errorf("dac service: %w", err) - } + dac := dynamicaccess.NewController(ctx, actLogic, localStore.ChunkStore(), localStore.Cache()) b.dacCloser = dac var ( From 953ad35d0108dabb0f0f3fe9f2c8a88cbb8e1892 Mon Sep 17 00:00:00 2001 From: Kexort Date: Mon, 22 Apr 2024 14:12:31 +0200 Subject: [PATCH 02/14] Add POST endpoint + fixes --- pkg/api/dynamicaccess.go | 93 ++++++++++++++++++++++++++++--- pkg/api/dynamicaccess_test.go | 62 ++++++++++++++++----- pkg/api/router.go | 6 ++ pkg/dynamicaccess/controller.go | 9 +-- pkg/dynamicaccess/grantee_test.go | 20 +++++-- 5 files changed, 160 insertions(+), 30 deletions(-) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index ed68d4cbb5c..564c8a64815 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -38,6 +38,19 @@ type GranteesPatchRequest struct { Revokelist []string `json:"revoke"` } +type GranteesPatchResponse struct { + Reference swarm.Address `json:"ref"` + HistoryReference swarm.Address `json:"historyref"` +} + +type GranteesPostRequest struct { + GranteeList []string `json:"grantees"` +} + +type GranteesPostResponse struct { + Reference swarm.Address `json:"ref"` + HistoryReference swarm.Address `json:"historyref"` +} type GranteesPatch struct { Addlist []ecdsa.PublicKey Revokelist []ecdsa.PublicKey @@ -134,7 +147,7 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) paths := struct { GranteesAddress swarm.Address `map:"address,resolve" validate:"required"` }{} - if response := s.mapStructure(r.Header, &paths); response != nil { + if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) return } @@ -162,15 +175,14 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) paths := struct { GranteesAddress swarm.Address `map:"address,resolve" validate:"required"` }{} - if response := s.mapStructure(r.Header, &paths); response != nil { + if response := s.mapStructure(mux.Vars(r), &paths); response != nil { 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"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address" validate:"required"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -222,10 +234,77 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) }) granteeref := paths.GranteesAddress - granteeref, historyref, _ := s.dac.HandleGrantees(ctx, granteeref, *headers.HistoryAddress, headers.Publisher, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) + granteeref, historyref, _ := s.dac.HandleGrantees(ctx, granteeref, *headers.HistoryAddress, &s.publicKey, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) + putter.Done(granteeref) + putter.Done(historyref) + jsonhttp.OK(w, GranteesPatchResponse{ + Reference: granteeref, + HistoryReference: historyref, + }) +} + +func (s *Service) actCreateGranteesHandler(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 + } + + headers := struct { + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + }{} + 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 := GranteesPostRequest{} + 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 + } + } + + list := make([]ecdsa.PublicKey, len(gpr.GranteeList)) + for _, g := range gpr.GranteeList { + h, _ := hex.DecodeString(g) + k, _ := btcec.ParsePubKey(h) + list = append(list, *k.ToECDSA()) + } + tag, _ := s.getOrCreateSessionID(0) + + ctx := r.Context() + putter, _ := s.newStamperPutter(ctx, putterOptions{ + BatchID: headers.BatchID, + TagID: tag, + Pin: false, + Deferred: true, + }) + + granteeref, historyref, _ := s.dac.HandleGrantees(ctx, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, convertToPointerSlice(list), nil) putter.Done(granteeref) putter.Done(historyref) - jsonhttp.OK(w, nil) + jsonhttp.Created(w, GranteesPostResponse{ + Reference: granteeref, + HistoryReference: historyref, + }) } func convertToPointerSlice(slice []ecdsa.PublicKey) []*ecdsa.PublicKey { diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index a9dc706c2b7..4405076b4c8 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -808,8 +808,6 @@ func TestDacGrantees(t *testing.T) { 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 @@ -841,14 +839,20 @@ func TestDacGrantees(t *testing.T) { }), ) }) - t.Run("get-grantees-wrong-address", func(t *testing.T) { - }) t.Run("add-revoke-grantees", func(t *testing.T) { + body := api.GranteesPatchRequest{ + Addlist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + Revokelist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + } + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, addr.String()), + jsonhttptest.WithJSONRequestBody(body), + ) }) 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", @@ -857,23 +861,55 @@ func TestDacGrantees(t *testing.T) { ) }) t.Run("add-grantee-with-history", func(t *testing.T) { + body := api.GranteesPatchRequest{ + Addlist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + } + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, addr.String()), + jsonhttptest.WithJSONRequestBody(body), + ) }) 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.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), jsonhttptest.WithJSONRequestBody(body), ) - expected := []string{ - "03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2", - "03c712a7e29bc792ac8d8ae49793d28d5bda27ed70f0d90697b2fb456c0a168bd2", - "032541acf966823bae26c2c16a7102e728ade3e2e29c11a8a17b29d8eb2bd19302", - "02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4", + }) + t.Run("create-granteelist", func(t *testing.T) { + body := api.GranteesPostRequest{ + GranteeList: []string{ + "02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4", + "03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2", + }, } - jsonhttptest.Request(t, client, http.MethodGet, "/grantee/"+addr.String(), http.StatusOK, - jsonhttptest.WithExpectedJSONResponse(expected), + jsonhttptest.Request(t, client, http.MethodPost, "/grantee", http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithJSONRequestBody(body), + ) + }) + t.Run("create-granteelist-without-stamp", func(t *testing.T) { + body := api.GranteesPostRequest{ + GranteeList: []string{ + "03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2", + }, + } + jsonhttptest.Request(t, client, http.MethodPost, "/grantee", http.StatusBadRequest, + jsonhttptest.WithJSONRequestBody(body), + ) + }) + t.Run("create-granteelist-empty-body", func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodPost, "/grantee", http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(bytes.NewReader(nil)), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "could not validate request", + Code: http.StatusBadRequest, + }), ) }) } diff --git a/pkg/api/router.go b/pkg/api/router.go index 1a2040a2639..8f3794b6828 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -266,6 +266,12 @@ func (s *Service) mountAPI() { ), }) + handle("/grantee", jsonhttp.MethodHandler{ + "POST": web.ChainHandlers( + web.FinalHandlerFunc(s.actCreateGranteesHandler), + ), + }) + handle("/grantee/{address}", jsonhttp.MethodHandler{ "GET": web.ChainHandlers( web.FinalHandlerFunc(s.actListGranteesHandler), diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index f6538af59c4..382b8fc463d 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -18,11 +18,7 @@ import ( const granteeListEncrypt = true type GranteeManager interface { - //PATCH /grantees - //{publisher, addList, removeList} HandleGrantees(ctx context.Context, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) - - //GET /grantees/{history root hash} GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) } @@ -181,12 +177,13 @@ func (c *controller) HandleGrantees(ctx context.Context, granteeref swarm.Addres gl.Add(addList) gl.Remove(removeList) - granteesToAdd := addList - + var granteesToAdd []*ecdsa.PublicKey // generate new access key and new act if len(removeList) != 0 || granteeref.IsZero() { c.accessLogic.AddPublisher(ctx, act, publisher) granteesToAdd = gl.Get() + } else { + granteesToAdd = addList } for _, grantee := range granteesToAdd { diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 0578be28ff4..573171ba9d9 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -3,10 +3,10 @@ package dynamicaccess_test import ( "context" "crypto/ecdsa" - "crypto/elliptic" "crypto/rand" "testing" + "github.com/btcsuite/btcd/btcec/v2" "github.com/ethersphere/bee/v2/pkg/dynamicaccess" "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/file/loadsave" @@ -31,9 +31,9 @@ func createLs() file.LoadSaver { } func generateKeyListFixture() ([]*ecdsa.PublicKey, error) { - key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - key3, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key1, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) + key2, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) + key3, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) if err != nil { return nil, err } @@ -195,3 +195,15 @@ func TestGranteeSave(t *testing.T) { assert.Equal(t, append(keys, keys...), val) }) } + +func TestGranteeRemoveTwo(t *testing.T) { + gl := dynamicaccess.NewGranteeList(createLs()) + keys, err := generateKeyListFixture() + if err != nil { + t.Errorf("key generation error: %v", err) + } + err = gl.Add([]*ecdsa.PublicKey{keys[0]}) + err = gl.Add([]*ecdsa.PublicKey{keys[0]}) + err = gl.Remove([]*ecdsa.PublicKey{keys[0]}) + assert.NoError(t, err) +} From 4697a2e84c782339f76a2c900885f78ba8cca5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= Date: Mon, 22 Apr 2024 14:44:16 +0200 Subject: [PATCH 03/14] Save grantees as pubkey list and fix remove error; CHG: act-handler logger names --- pkg/api/bytes.go | 2 +- pkg/api/bzz.go | 2 +- pkg/api/chunk.go | 2 +- pkg/api/dirs.go | 2 +- pkg/api/dynamicaccess.go | 9 ++- pkg/api/feed.go | 2 +- pkg/api/soc.go | 2 +- pkg/dynamicaccess/grantee.go | 97 ++++++++++++++++--------------- pkg/dynamicaccess/grantee_test.go | 8 +-- 9 files changed, 65 insertions(+), 61 deletions(-) diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index a84ec2936de..2afb1db99c0 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -118,7 +118,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := reference if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, reference, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index 6319624a17d..b49bc89baea 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -266,7 +266,7 @@ func (s *Service) fileUploadHandler( encryptedReference := manifestReference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, manifestReference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, manifestReference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index 496f7f8d306..21daa0d0f57 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -143,7 +143,7 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := chunk.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, chunk.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, chunk.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dirs.go b/pkg/api/dirs.go index c85c68e4edb..f187fbde01e 100644 --- a/pkg/api/dirs.go +++ b/pkg/api/dirs.go @@ -102,7 +102,7 @@ func (s *Service) dirUploadHandler( encryptedReference := reference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, reference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 564c8a64815..5b2b78f6888 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -11,7 +11,6 @@ import ( "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" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/gorilla/mux" @@ -61,7 +60,7 @@ type GranteesPatch struct { func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logger := s.logger.WithName("acthandler").Build() + logger := s.logger.WithName("act_decryption_handler").Build() paths := struct { Address swarm.Address `map:"address,resolve" validate:"required"` }{} @@ -101,12 +100,12 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { // Uploads the encrypted reference, history and kvs to the store func (s *Service) actEncryptionHandler( ctx context.Context, - logger log.Logger, w http.ResponseWriter, putter storer.PutterSession, reference swarm.Address, historyRootHash swarm.Address, ) (swarm.Address, error) { + logger := s.logger.WithName("act_encryption_handler").Build() publisherPublicKey := &s.publicKey storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, reference, publisherPublicKey, historyRootHash) if err != nil { @@ -143,7 +142,7 @@ func (s *Service) actEncryptionHandler( } func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) { - logger := s.logger.WithName("acthandler").Build() + logger := s.logger.WithName("act_list_grantees_handler").Build() paths := struct { GranteesAddress swarm.Address `map:"address,resolve" validate:"required"` }{} @@ -164,7 +163,7 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) } func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) { - logger := s.logger.WithName("acthandler").Build() + logger := s.logger.WithName("act_grant_revoke_handler").Build() if r.Body == http.NoBody { logger.Error(nil, "request has no body") diff --git a/pkg/api/feed.go b/pkg/api/feed.go index a2679547478..3d43d3d148e 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -249,7 +249,7 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { // TODO: do we want to allow feed act upload/ download? encryptedReference := ref if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, ref, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, ref, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/soc.go b/pkg/api/soc.go index c2f11d6e05c..29777066b10 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -159,7 +159,7 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := sch.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, sch.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, sch.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index 514dd2a550e..b0a1a3799cd 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -23,62 +23,28 @@ type GranteeList interface { } type GranteeListStruct struct { - grantees []byte + grantees []*ecdsa.PublicKey loadSave file.LoadSaver } var _ GranteeList = (*GranteeListStruct)(nil) func (g *GranteeListStruct) Get() []*ecdsa.PublicKey { - return g.deserialize(g.grantees) -} - -func (g *GranteeListStruct) serialize(publicKeys []*ecdsa.PublicKey) []byte { - b := make([]byte, 0, len(publicKeys)*publicKeyLen) - for _, key := range publicKeys { - b = append(b, g.serializePublicKey(key)...) - } - return b -} - -func (g *GranteeListStruct) serializePublicKey(pub *ecdsa.PublicKey) []byte { - return elliptic.Marshal(pub.Curve, pub.X, pub.Y) -} - -func (g *GranteeListStruct) deserialize(data []byte) []*ecdsa.PublicKey { - if len(data) == 0 { - return nil - } - - p := make([]*ecdsa.PublicKey, 0, len(data)/publicKeyLen) - for i := 0; i < len(data); i += publicKeyLen { - pubKey := g.deserializeBytes(data[i : i+publicKeyLen]) - if pubKey == nil { - return nil - } - p = append(p, pubKey) - } - return p -} - -func (g *GranteeListStruct) deserializeBytes(data []byte) *ecdsa.PublicKey { - curve := btcec.S256() - x, y := elliptic.Unmarshal(curve, data) - return &ecdsa.PublicKey{Curve: curve, X: x, Y: y} + return g.grantees } func (g *GranteeListStruct) Add(addList []*ecdsa.PublicKey) error { if len(addList) == 0 { return fmt.Errorf("no public key provided") } + g.grantees = append(g.grantees, addList...) - data := g.serialize(addList) - g.grantees = append(g.grantees, data...) return nil } func (g *GranteeListStruct) Save(ctx context.Context) (swarm.Address, error) { - refBytes, err := g.loadSave.Save(ctx, g.grantees) + data := serialize(g.grantees) + refBytes, err := g.loadSave.Save(ctx, data) if err != nil { return swarm.ZeroAddress, fmt.Errorf("grantee save error: %w", err) } @@ -90,26 +56,28 @@ func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error { if len(keysToRemove) == 0 { return fmt.Errorf("nothing to remove") } - grantees := g.deserialize(g.grantees) - if grantees == nil { + + if len(g.grantees) == 0 { return fmt.Errorf("no grantee found") } + grantees := g.grantees for _, remove := range keysToRemove { - for i, grantee := range grantees { - if grantee.Equal(remove) { + for i := 0; i < len(grantees); i++ { + if grantees[i].Equal(remove) { grantees[i] = grantees[len(grantees)-1] grantees = grantees[:len(grantees)-1] } } } - g.grantees = g.serialize(grantees) + g.grantees = grantees + return nil } func NewGranteeList(ls file.LoadSaver) GranteeList { return &GranteeListStruct{ - grantees: []byte{}, + grantees: []*ecdsa.PublicKey{}, loadSave: ls, } } @@ -119,9 +87,46 @@ func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) Grantee if err != nil { return nil } + grantees := deserialize(data) return &GranteeListStruct{ - grantees: data, + grantees: grantees, loadSave: ls, } } + +func serialize(publicKeys []*ecdsa.PublicKey) []byte { + b := make([]byte, 0, len(publicKeys)*publicKeyLen) + for _, key := range publicKeys { + b = append(b, serializePublicKey(key)...) + } + return b +} + +func serializePublicKey(pub *ecdsa.PublicKey) []byte { + return elliptic.Marshal(pub.Curve, pub.X, pub.Y) +} + +func deserialize(data []byte) []*ecdsa.PublicKey { + if len(data) == 0 { + return []*ecdsa.PublicKey{} + } + + p := make([]*ecdsa.PublicKey, 0, len(data)/publicKeyLen) + for i := 0; i < len(data); i += publicKeyLen { + pubKey := deserializeBytes(data[i : i+publicKeyLen]) + if pubKey == nil { + return []*ecdsa.PublicKey{} + } + p = append(p, pubKey) + } + return p +} + +func deserializeBytes(data []byte) *ecdsa.PublicKey { + key, err := btcec.ParsePubKey(data) + if err != nil { + return nil + } + return key.ToECDSA() +} diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 573171ba9d9..8782a96bc45 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -49,7 +49,7 @@ func TestGranteeAddGet(t *testing.T) { t.Run("Get empty grantee list should return error", func(t *testing.T) { val := gl.Get() - assert.Nil(t, val) + assert.Empty(t, val) }) t.Run("Get should return value equal to put value", func(t *testing.T) { @@ -128,19 +128,19 @@ func TestGranteeRemove(t *testing.T) { err := gl.Remove(removeList2) assert.NoError(t, err) retVal := gl.Get() - assert.Nil(t, retVal) + assert.Empty(t, retVal) }) t.Run("Remove from empty grantee list should return error", func(t *testing.T) { err := gl.Remove(removeList1) assert.Error(t, err) retVal := gl.Get() - assert.Nil(t, retVal) + assert.Empty(t, retVal) }) t.Run("Remove empty remove list should return error", func(t *testing.T) { err := gl.Remove(nil) assert.Error(t, err) retVal := gl.Get() - assert.Nil(t, retVal) + assert.Empty(t, retVal) }) } From 8df33ecf32cb27609e8cfdaa6dfcc480aa813be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= Date: Tue, 23 Apr 2024 17:56:24 +0200 Subject: [PATCH 04/14] Refactor: pass getter, putter to controller functions --- pkg/api/bytes.go | 2 +- pkg/api/bzz.go | 2 +- pkg/api/chunk.go | 2 +- pkg/api/dirs.go | 2 +- pkg/api/dynamicaccess.go | 86 +++++++++++++++++++++++----- pkg/api/feed.go | 2 +- pkg/api/soc.go | 2 +- pkg/dynamicaccess/controller.go | 67 +++++++++++++++------- pkg/dynamicaccess/controller_test.go | 30 +++++----- pkg/dynamicaccess/mock/service.go | 8 +-- pkg/node/devnode.go | 2 +- pkg/node/node.go | 2 +- 12 files changed, 147 insertions(+), 60 deletions(-) diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index 2afb1db99c0..449eabac0e1 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -118,7 +118,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := reference if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, reference, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index b49bc89baea..75a56be94a3 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -266,7 +266,7 @@ func (s *Service) fileUploadHandler( encryptedReference := manifestReference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, manifestReference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, manifestReference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index 21daa0d0f57..7d98efcb30a 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -143,7 +143,7 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := chunk.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, chunk.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, chunk.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dirs.go b/pkg/api/dirs.go index f187fbde01e..66f20f1a137 100644 --- a/pkg/api/dirs.go +++ b/pkg/api/dirs.go @@ -102,7 +102,7 @@ func (s *Service) dirUploadHandler( encryptedReference := reference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, reference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 5b2b78f6888..2e7c28e7b03 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -5,12 +5,15 @@ import ( "crypto/ecdsa" "encoding/hex" "encoding/json" + "errors" "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/postage" + storage "github.com/ethersphere/bee/v2/pkg/storage" storer "github.com/ethersphere/bee/v2/pkg/storer" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/gorilla/mux" @@ -85,7 +88,7 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { return } ctx := r.Context() - reference, err := s.dac.DownloadHandler(ctx, paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) + reference, err := s.dac.DownloadHandler(ctx, s.storer.Download(true), paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) if err != nil { jsonhttp.InternalServerError(w, errActDownload) return @@ -101,13 +104,14 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { func (s *Service) actEncryptionHandler( ctx context.Context, w http.ResponseWriter, + getter storage.Getter, putter storer.PutterSession, reference swarm.Address, historyRootHash swarm.Address, ) (swarm.Address, error) { logger := s.logger.WithName("act_encryption_handler").Build() publisherPublicKey := &s.publicKey - storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, reference, publisherPublicKey, historyRootHash) + storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, getter, putter, reference, publisherPublicKey, historyRootHash) if err != nil { logger.Debug("act failed to encrypt reference", "error", err) logger.Error(nil, "act failed to encrypt reference") @@ -137,7 +141,6 @@ func (s *Service) actEncryptionHandler( } w.Header().Set(SwarmActHistoryAddressHeader, historyReference.String()) - return encryptedReference, nil } @@ -150,7 +153,7 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) response("invalid path params", logger, w) return } - grantees, err := s.dac.GetGrantees(r.Context(), paths.GranteesAddress) + grantees, err := s.dac.GetGrantees(r.Context(), s.storer.Download(true), paths.GranteesAddress) if err != nil { jsonhttp.NotFound(w, "grantee list not found") return @@ -225,15 +228,32 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) tag, _ := s.getOrCreateSessionID(0) ctx := r.Context() - putter, _ := s.newStamperPutter(ctx, putterOptions{ + putter, err := s.newStamperPutter(ctx, putterOptions{ BatchID: headers.BatchID, TagID: tag, Pin: false, - Deferred: true, + Deferred: false, }) + if err != nil { + logger.Debug("putter failed", "error", err) + logger.Error(nil, "putter failed") + switch { + case errors.Is(err, errBatchUnusable) || errors.Is(err, postage.ErrNotUsable): + jsonhttp.UnprocessableEntity(w, "batch not usable yet or does not exist") + case errors.Is(err, postage.ErrNotFound): + jsonhttp.NotFound(w, "batch with id not found") + case errors.Is(err, errInvalidPostageBatch): + jsonhttp.BadRequest(w, "invalid batch id") + case errors.Is(err, errUnsupportedDevNodeOperation): + jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation) + default: + jsonhttp.BadRequest(w, nil) + } + return + } granteeref := paths.GranteesAddress - granteeref, historyref, _ := s.dac.HandleGrantees(ctx, granteeref, *headers.HistoryAddress, &s.publicKey, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) + granteeref, historyref, _ := s.dac.HandleGrantees(ctx, s.storer.Download(true), putter, granteeref, *headers.HistoryAddress, &s.publicKey, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) putter.Done(granteeref) putter.Done(historyref) jsonhttp.OK(w, GranteesPatchResponse{ @@ -290,16 +310,55 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques tag, _ := s.getOrCreateSessionID(0) ctx := r.Context() - putter, _ := s.newStamperPutter(ctx, putterOptions{ + // TODO: pin and deferred headers + putter, err := s.newStamperPutter(ctx, putterOptions{ BatchID: headers.BatchID, TagID: tag, Pin: false, - Deferred: true, + Deferred: false, }) + if err != nil { + logger.Debug("putter failed", "error", err) + logger.Error(nil, "putter failed") + switch { + case errors.Is(err, errBatchUnusable) || errors.Is(err, postage.ErrNotUsable): + jsonhttp.UnprocessableEntity(w, "batch not usable yet or does not exist") + case errors.Is(err, postage.ErrNotFound): + jsonhttp.NotFound(w, "batch with id not found") + case errors.Is(err, errInvalidPostageBatch): + jsonhttp.BadRequest(w, "invalid batch id") + case errors.Is(err, errUnsupportedDevNodeOperation): + jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation) + default: + jsonhttp.BadRequest(w, nil) + } + return + } + + granteeref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.Download(true), putter, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, convertToPointerSlice(list), nil) + if err != nil { + logger.Info("HandleGrantees error", "error", err) + logger.Error(nil, "HandleGrantees error") + jsonhttp.InternalServerError(w, "HandleGrantees error") + return + } + + err = putter.Done(granteeref) + if err != nil { + logger.Info("putter granteeref error", "error", err) + logger.Error(nil, "putter granteeref error") + jsonhttp.InternalServerError(w, "putter granteeref error") + return + } + + err = putter.Done(historyref) + if err != nil { + logger.Info("putter historyref error", "error", err) + logger.Error(nil, "putter historyref error") + jsonhttp.InternalServerError(w, "putter historyref error") + return + } - granteeref, historyref, _ := s.dac.HandleGrantees(ctx, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, convertToPointerSlice(list), nil) - putter.Done(granteeref) - putter.Done(historyref) jsonhttp.Created(w, GranteesPostResponse{ Reference: granteeref, HistoryReference: historyref, @@ -309,7 +368,8 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques func convertToPointerSlice(slice []ecdsa.PublicKey) []*ecdsa.PublicKey { pointerSlice := make([]*ecdsa.PublicKey, len(slice)) for i, key := range slice { - pointerSlice[i] = &key + tempKey := key + pointerSlice[i] = &tempKey } return pointerSlice } diff --git a/pkg/api/feed.go b/pkg/api/feed.go index 3d43d3d148e..6f6b0a5c955 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -249,7 +249,7 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { // TODO: do we want to allow feed act upload/ download? encryptedReference := ref if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, ref, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, ref, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/soc.go b/pkg/api/soc.go index 29777066b10..f61463584ac 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -159,7 +159,7 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := sch.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, sch.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, sch.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 382b8fc463d..003d897a4ca 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -18,37 +18,36 @@ import ( const granteeListEncrypt = true type GranteeManager interface { - HandleGrantees(ctx context.Context, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) - GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) + HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) + GetGrantees(ctx context.Context, getter storage.Getter, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) } // TODO: add granteeList ref to history metadata to solve inconsistency type Controller interface { GranteeManager // DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp. - DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) + DownloadHandler(ctx context.Context, getter storage.Getter, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) // TODO: history encryption // UploadHandler encrypts the reference and stores it in the history as the latest update. - UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + UploadHandler(ctx context.Context, getter storage.Getter, putter storage.Putter, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) io.Closer } type controller struct { accessLogic ActLogic - getter storage.Getter - putter storage.Putter } var _ Controller = (*controller)(nil) func (c *controller) DownloadHandler( ctx context.Context, + getter storage.Getter, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64, ) (swarm.Address, error) { - ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) + ls := loadsave.NewReadonly(getter) history, err := NewHistoryReference(ls, historyRootHash) if err != nil { return swarm.ZeroAddress, err @@ -68,11 +67,13 @@ func (c *controller) DownloadHandler( func (c *controller) UploadHandler( ctx context.Context, + getter storage.Getter, + putter storage.Putter, refrefence swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, ) (swarm.Address, swarm.Address, swarm.Address, error) { - ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) + ls := loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, false, redundancy.NONE)) historyRef := historyRootHash var ( storage kvs.KeyValueStore @@ -126,21 +127,28 @@ func (c *controller) UploadHandler( return actRef, historyRef, encryptedRef, err } -func NewController(ctx context.Context, accessLogic ActLogic, getter storage.Getter, putter storage.Putter) Controller { +func NewController(ctx context.Context, accessLogic ActLogic) Controller { return &controller{ accessLogic: accessLogic, - getter: getter, - putter: putter, } } -func (c *controller) HandleGrantees(ctx context.Context, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { +func (c *controller) HandleGrantees( + ctx context.Context, + getter storage.Getter, + putter storage.Putter, + granteeref swarm.Address, + historyref swarm.Address, + publisher *ecdsa.PublicKey, + addList []*ecdsa.PublicKey, + removeList []*ecdsa.PublicKey, +) (swarm.Address, swarm.Address, error) { var ( err error h History act kvs.KeyValueStore - ls = loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) - gls = loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, granteeListEncrypt, redundancy.NONE)) + ls = loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + gls = loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) ) if !historyref.IsZero() { h, err = NewHistoryReference(ls, historyref) @@ -174,20 +182,34 @@ func (c *controller) HandleGrantees(ctx context.Context, granteeref swarm.Addres } else { gl = NewGranteeListReference(gls, granteeref) } - gl.Add(addList) - gl.Remove(removeList) + err = gl.Add(addList) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + if len(removeList) != 0 { + err = gl.Remove(removeList) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + } var granteesToAdd []*ecdsa.PublicKey // generate new access key and new act if len(removeList) != 0 || granteeref.IsZero() { - c.accessLogic.AddPublisher(ctx, act, publisher) + err = c.accessLogic.AddPublisher(ctx, act, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } granteesToAdd = gl.Get() } else { granteesToAdd = addList } for _, grantee := range granteesToAdd { - c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + err := c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } } actref, err := act.Save(ctx) @@ -195,7 +217,10 @@ func (c *controller) HandleGrantees(ctx context.Context, granteeref swarm.Addres return swarm.ZeroAddress, swarm.ZeroAddress, err } - h.Add(ctx, actref, nil, nil) + err = h.Add(ctx, actref, nil, nil) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } href, err := h.Store(ctx) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, err @@ -208,8 +233,8 @@ func (c *controller) HandleGrantees(ctx context.Context, granteeref swarm.Addres return glref, href, nil } -func (c *controller) GetGrantees(ctx context.Context, granteeRef swarm.Address) ([]*ecdsa.PublicKey, error) { - ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, granteeListEncrypt, redundancy.NONE)) +func (c *controller) GetGrantees(ctx context.Context, getter storage.Getter, granteeRef swarm.Address) ([]*ecdsa.PublicKey, error) { + ls := loadsave.NewReadonly(getter) gl := NewGranteeListReference(ls, granteeRef) return gl.Get(), nil } diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 55131d3793b..3d5b4a5b850 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -47,9 +47,9 @@ func TestController_NewUpload(t *testing.T) { publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) - c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + c := dynamicaccess.NewController(ctx, al) ref := swarm.RandAddress(t) - _, hRef, encRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, swarm.ZeroAddress) + _, hRef, encRef, err := c.UploadHandler(ctx, mockStorer.ChunkStore(), mockStorer.Cache(), ref, &publisher.PublicKey, swarm.ZeroAddress) ls := createLs() h, err := dynamicaccess.NewHistoryReference(ls, hRef) @@ -68,7 +68,7 @@ func TestController_PublisherDownload(t *testing.T) { publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) - c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + c := dynamicaccess.NewController(ctx, al) ls := createLs() ref := swarm.RandAddress(t) href, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) @@ -79,7 +79,7 @@ func TestController_PublisherDownload(t *testing.T) { encRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, encRef, &publisher.PublicKey, href, time.Now().Unix()) + dref, err := c.DownloadHandler(ctx, mockStorer.ChunkStore(), encRef, &publisher.PublicKey, href, time.Now().Unix()) assert.NoError(t, err) assert.Equal(t, ref, dref) } @@ -94,7 +94,7 @@ func TestController_GranteeDownload(t *testing.T) { diffieHellman := dynamicaccess.NewDefaultSession(grantee) al := dynamicaccess.NewLogic(diffieHellman) ls := createLs() - c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + c := dynamicaccess.NewController(ctx, al) ref := swarm.RandAddress(t) href, err := getHistoryFixture(ctx, ls, publisherAL, &publisher.PublicKey) h, err := dynamicaccess.NewHistoryReference(ls, href) @@ -105,7 +105,7 @@ func TestController_GranteeDownload(t *testing.T) { encRef, err := publisherAL.EncryptRef(ctx, act, &publisher.PublicKey, ref) assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, encRef, &publisher.PublicKey, href, ts) + dref, err := c.DownloadHandler(ctx, mockStorer.ChunkStore(), encRef, &publisher.PublicKey, href, ts) assert.NoError(t, err) assert.Equal(t, ref, dref) } @@ -116,7 +116,9 @@ func TestController_HandleGrantees(t *testing.T) { diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) ls := createLs() - c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + getter := mockStorer.ChunkStore() + putter := mockStorer.Cache() + c := dynamicaccess.NewController(ctx, al) href, _ := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) grantee1 := getPrivKey(0) @@ -124,7 +126,7 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add to new list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, err := c.HandleGrantees(ctx, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) @@ -133,7 +135,7 @@ func TestController_HandleGrantees(t *testing.T) { }) t.Run("add to existing list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, err := c.HandleGrantees(ctx, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) @@ -141,7 +143,7 @@ func TestController_HandleGrantees(t *testing.T) { assert.Len(t, gl.Get(), 1) addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} - granteeRef, _, err = c.HandleGrantees(ctx, granteeRef, href, &publisher.PublicKey, addList, nil) + granteeRef, _, err = c.HandleGrantees(ctx, getter, putter, granteeRef, href, &publisher.PublicKey, addList, nil) gl = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) @@ -153,7 +155,7 @@ func TestController_HandleGrantees(t *testing.T) { gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) granteeRef, err := gl.Save(ctx) - granteeRef, _, err = c.HandleGrantees(ctx, granteeRef, href, &publisher.PublicKey, addList, revokeList) + granteeRef, _, err = c.HandleGrantees(ctx, getter, putter, granteeRef, href, &publisher.PublicKey, addList, revokeList) gl = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -162,8 +164,8 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add twice", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} - granteeRef, _, err := c.HandleGrantees(ctx, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - granteeRef, _, err = c.HandleGrantees(ctx, granteeRef, href, &publisher.PublicKey, addList, nil) + granteeRef, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, err = c.HandleGrantees(ctx, getter, putter, granteeRef, href, &publisher.PublicKey, addList, nil) gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -171,7 +173,7 @@ func TestController_HandleGrantees(t *testing.T) { }) t.Run("revoke non-existing", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, err := c.HandleGrantees(ctx, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) diff --git a/pkg/dynamicaccess/mock/service.go b/pkg/dynamicaccess/mock/service.go index 485de90f6a9..15f26ac6f00 100644 --- a/pkg/dynamicaccess/mock/service.go +++ b/pkg/dynamicaccess/mock/service.go @@ -78,7 +78,7 @@ func WithPublisher(ref string) Option { }) } -func (m *mockDacService) DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { +func (m *mockDacService) DownloadHandler(ctx context.Context, getter storage.Getter, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { if m.acceptAll { return swarm.ParseHexAddress("36e6c1bbdfee6ac21485d5f970479fd1df458d36df9ef4e8179708ed46da557f") } @@ -101,7 +101,7 @@ func (m *mockDacService) DownloadHandler(ctx context.Context, encryptedRef swarm return m.refMap[encryptedRef.String()], nil } -func (m *mockDacService) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockDacService) UploadHandler(ctx context.Context, getter storage.Getter, putter storage.Putter, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") kvsRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") if m.acceptAll { @@ -141,12 +141,12 @@ func (m *mockDacService) Close() error { return nil } -func (m *mockDacService) HandleGrantees(ctx context.Context, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { +func (m *mockDacService) HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") return glRef, historyRef, nil } -func (m *mockDacService) GetGrantees(ctx context.Context, granteeref swarm.Address) ([]*ecdsa.PublicKey, error) { +func (m *mockDacService) GetGrantees(ctx context.Context, getter storage.Getter, granteeref swarm.Address) ([]*ecdsa.PublicKey, error) { keys := []string{ "a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa", "b786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfb", diff --git a/pkg/node/devnode.go b/pkg/node/devnode.go index 4e96a301769..b7ac3dfee56 100644 --- a/pkg/node/devnode.go +++ b/pkg/node/devnode.go @@ -238,7 +238,7 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { session := dynamicaccess.NewDefaultSession(mockKey) actLogic := dynamicaccess.NewLogic(session) - dac := dynamicaccess.NewController(context.Background(), actLogic, localStore.ChunkStore(), localStore.Cache()) + dac := dynamicaccess.NewController(context.Background(), actLogic) b.dacCloser = dac pssService := pss.New(mockKey, logger) diff --git a/pkg/node/node.go b/pkg/node/node.go index f16f2579e85..b48e97ddafc 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -776,7 +776,7 @@ func NewBee( evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) } actLogic := dynamicaccess.NewLogic(session) - dac := dynamicaccess.NewController(ctx, actLogic, localStore.ChunkStore(), localStore.Cache()) + dac := dynamicaccess.NewController(ctx, actLogic) b.dacCloser = dac var ( From 5f219fe49452984cd621cedc63c1c9864e1d00db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= Date: Tue, 23 Apr 2024 18:29:03 +0200 Subject: [PATCH 05/14] Refactor: error handling in dynamicaccess; Read cache header only for download handlers --- pkg/api/bytes.go | 2 +- pkg/api/bzz.go | 2 +- pkg/api/chunk.go | 2 +- pkg/api/dirs.go | 2 +- pkg/api/dynamicaccess.go | 134 +++++++++++++++++++++++++++++++-------- pkg/api/feed.go | 2 +- pkg/api/soc.go | 2 +- 7 files changed, 115 insertions(+), 31 deletions(-) diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index 449eabac0e1..a2d65cae1ba 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -118,7 +118,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := reference if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, reference, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, reference, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index 75a56be94a3..7f322be0720 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -266,7 +266,7 @@ func (s *Service) fileUploadHandler( encryptedReference := manifestReference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, manifestReference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, manifestReference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index 7d98efcb30a..9c462b1a558 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -143,7 +143,7 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := chunk.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, chunk.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, chunk.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dirs.go b/pkg/api/dirs.go index 66f20f1a137..e871eee2e8b 100644 --- a/pkg/api/dirs.go +++ b/pkg/api/dirs.go @@ -102,7 +102,7 @@ func (s *Service) dirUploadHandler( encryptedReference := reference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, reference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, reference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 2e7c28e7b03..60d3a1c5fab 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -76,6 +76,7 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { Timestamp *int64 `map:"Swarm-Act-Timestamp"` Publisher *ecdsa.PublicKey `map:"Swarm-Act-Publisher"` HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + Cache *bool `map:"Swarm-Cache"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -87,8 +88,13 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { h.ServeHTTP(w, r) return } + + cache := true + if headers.Cache != nil { + cache = *headers.Cache + } ctx := r.Context() - reference, err := s.dac.DownloadHandler(ctx, s.storer.Download(true), paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) + reference, err := s.dac.DownloadHandler(ctx, s.storer.Download(cache), paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) if err != nil { jsonhttp.InternalServerError(w, errActDownload) return @@ -153,7 +159,19 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) response("invalid path params", logger, w) return } - grantees, err := s.dac.GetGrantees(r.Context(), s.storer.Download(true), paths.GranteesAddress) + + headers := struct { + Cache *bool `map:"Swarm-Cache"` + }{} + if response := s.mapStructure(r.Header, &headers); response != nil { + response("invalid header params", logger, w) + return + } + cache := true + if headers.Cache != nil { + cache = *headers.Cache + } + grantees, err := s.dac.GetGrantees(r.Context(), s.storer.Download(cache), paths.GranteesAddress) if err != nil { jsonhttp.NotFound(w, "grantee list not found") return @@ -214,18 +232,36 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) } grantees := GranteesPatch{} - for _, g := range gpr.Addlist { - h, _ := hex.DecodeString(g) - k, _ := btcec.ParsePubKey(h) - grantees.Addlist = append(grantees.Addlist, *k.ToECDSA()) + paresAddlist, err := parseKeys(gpr.Addlist) + if err != nil { + logger.Debug("add list key parse failed", "error", err) + logger.Error(nil, "add list key parse failed") + jsonhttp.InternalServerError(w, "error add list key parsing") + return } - for _, g := range gpr.Revokelist { - h, _ := hex.DecodeString(g) - k, _ := btcec.ParsePubKey(h) - grantees.Revokelist = append(grantees.Revokelist, *k.ToECDSA()) + grantees.Addlist = append(grantees.Addlist, paresAddlist...) + + paresRevokelist, err := parseKeys(gpr.Revokelist) + if err != nil { + logger.Debug("revoke list key parse failed", "error", err) + logger.Error(nil, "revoke list key parse failed") + jsonhttp.InternalServerError(w, "error revoke list key parsing") + return } + grantees.Revokelist = append(grantees.Revokelist, paresRevokelist...) - tag, _ := s.getOrCreateSessionID(0) + tag, err := s.getOrCreateSessionID(0) + if err != nil { + logger.Debug("get or create tag failed", "error", err) + logger.Error(nil, "get or create tag failed") + switch { + case errors.Is(err, storage.ErrNotFound): + jsonhttp.NotFound(w, "tag not found") + default: + jsonhttp.InternalServerError(w, "cannot get or create tag") + } + return + } ctx := r.Context() putter, err := s.newStamperPutter(ctx, putterOptions{ @@ -253,9 +289,29 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) } granteeref := paths.GranteesAddress - granteeref, historyref, _ := s.dac.HandleGrantees(ctx, s.storer.Download(true), putter, granteeref, *headers.HistoryAddress, &s.publicKey, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) - putter.Done(granteeref) - putter.Done(historyref) + granteeref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, granteeref, *headers.HistoryAddress, &s.publicKey, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) + if err != nil { + logger.Debug("failed to update grantee list", "error", err) + logger.Error(nil, "failed to update grantee list") + jsonhttp.InternalServerError(w, "failed to update grantee list") + return + } + + err = putter.Done(granteeref) + if err != nil { + logger.Debug("done split grantees failed", "error", err) + logger.Error(nil, "done split grantees failed") + jsonhttp.InternalServerError(w, "done split grantees failed") + return + } + + err = putter.Done(historyref) + if err != nil { + logger.Debug("done split history failed", "error", err) + logger.Error(nil, "done split history failed") + jsonhttp.InternalServerError(w, "done split history failed") + return + } jsonhttp.OK(w, GranteesPatchResponse{ Reference: granteeref, HistoryReference: historyref, @@ -307,7 +363,18 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques k, _ := btcec.ParsePubKey(h) list = append(list, *k.ToECDSA()) } - tag, _ := s.getOrCreateSessionID(0) + tag, err := s.getOrCreateSessionID(0) + if err != nil { + logger.Debug("get or create tag failed", "error", err) + logger.Error(nil, "get or create tag failed") + switch { + case errors.Is(err, storage.ErrNotFound): + jsonhttp.NotFound(w, "tag not found") + default: + jsonhttp.InternalServerError(w, "cannot get or create tag") + } + return + } ctx := r.Context() // TODO: pin and deferred headers @@ -335,27 +402,27 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques return } - granteeref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.Download(true), putter, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, convertToPointerSlice(list), nil) + granteeref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, convertToPointerSlice(list), nil) if err != nil { - logger.Info("HandleGrantees error", "error", err) - logger.Error(nil, "HandleGrantees error") - jsonhttp.InternalServerError(w, "HandleGrantees error") + logger.Debug("failed to update grantee list", "error", err) + logger.Error(nil, "failed to update grantee list") + jsonhttp.InternalServerError(w, "failed to update grantee list") return } err = putter.Done(granteeref) if err != nil { - logger.Info("putter granteeref error", "error", err) - logger.Error(nil, "putter granteeref error") - jsonhttp.InternalServerError(w, "putter granteeref error") + logger.Debug("done split grantees failed", "error", err) + logger.Error(nil, "done split grantees failed") + jsonhttp.InternalServerError(w, "done split grantees failed") return } err = putter.Done(historyref) if err != nil { - logger.Info("putter historyref error", "error", err) - logger.Error(nil, "putter historyref error") - jsonhttp.InternalServerError(w, "putter historyref error") + logger.Debug("done split history failed", "error", err) + logger.Error(nil, "done split history failed") + jsonhttp.InternalServerError(w, "done split history failed") return } @@ -373,3 +440,20 @@ func convertToPointerSlice(slice []ecdsa.PublicKey) []*ecdsa.PublicKey { } return pointerSlice } + +func parseKeys(list []string) ([]ecdsa.PublicKey, error) { + parsedList := make([]ecdsa.PublicKey, len(list)) + for _, g := range list { + h, err := hex.DecodeString(g) + if err != nil { + return []ecdsa.PublicKey{}, err + } + k, err := btcec.ParsePubKey(h) + if err != nil { + return []ecdsa.PublicKey{}, err + } + parsedList = append(parsedList, *k.ToECDSA()) + } + + return parsedList, nil +} diff --git a/pkg/api/feed.go b/pkg/api/feed.go index 6f6b0a5c955..3e4b8d5c10e 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -249,7 +249,7 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { // TODO: do we want to allow feed act upload/ download? encryptedReference := ref if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, ref, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, ref, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/soc.go b/pkg/api/soc.go index f61463584ac..cf37432ed1f 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -159,7 +159,7 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := sch.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.Download(true), putter, sch.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, sch.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return From 17b09e4b67245158b5f8996e59be4a1853c51350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= Date: Fri, 26 Apr 2024 13:28:31 +0200 Subject: [PATCH 06/14] CHG: grantees ref is encrypted and added to history ref + tests --- pkg/api/dynamicaccess.go | 51 ++++--- pkg/api/dynamicaccess_test.go | 34 ++++- pkg/dynamicaccess/accesslogic.go | 16 ++- pkg/dynamicaccess/controller.go | 130 ++++++++++++------ pkg/dynamicaccess/controller_test.go | 79 ++++++++--- pkg/dynamicaccess/grantee.go | 36 ++++- pkg/dynamicaccess/grantee_test.go | 42 ++++-- .../mock/{service.go => controller.go} | 14 +- pkg/node/devnode.go | 2 +- pkg/node/node.go | 2 +- 10 files changed, 289 insertions(+), 117 deletions(-) rename pkg/dynamicaccess/mock/{service.go => controller.go} (93%) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 60d3a1c5fab..505e33ee950 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -139,6 +139,7 @@ func (s *Service) actEncryptionHandler( return swarm.ZeroAddress, err } } + // TODO: probably does not need to store the eref, just return it err = putter.Done(encryptedReference) if err != nil { logger.Debug("done split encrypted reference failed", "error", err) @@ -150,6 +151,8 @@ func (s *Service) actEncryptionHandler( return encryptedReference, nil } +// actListGranteesHandler is a middleware that decrypts the given address and returns the list of grantees, +// only the publisher is authorized to access the list func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("act_list_grantees_handler").Build() paths := struct { @@ -171,9 +174,12 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) if headers.Cache != nil { cache = *headers.Cache } - grantees, err := s.dac.GetGrantees(r.Context(), s.storer.Download(cache), paths.GranteesAddress) + publisher := &s.publicKey + grantees, err := s.dac.GetGrantees(r.Context(), s.storer.Download(cache), publisher, paths.GranteesAddress) if err != nil { - jsonhttp.NotFound(w, "grantee list not found") + logger.Debug("could not get grantees", "error", err) + logger.Error(nil, "could not get grantees") + jsonhttp.Unauthorized(w, "granteelist not found") return } granteeSlice := make([]string, len(grantees)) @@ -183,6 +189,7 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) jsonhttp.OK(w, granteeSlice) } +// TODO: actGrantRevokeHandler doc. func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("act_grant_revoke_handler").Build() @@ -289,7 +296,7 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) } granteeref := paths.GranteesAddress - granteeref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, granteeref, *headers.HistoryAddress, &s.publicKey, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) + granteeref, encryptedglref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, granteeref, *headers.HistoryAddress, &s.publicKey, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) if err != nil { logger.Debug("failed to update grantee list", "error", err) logger.Error(nil, "failed to update grantee list") @@ -297,6 +304,14 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) return } + err = putter.Done(historyref) + if err != nil { + logger.Debug("done split history failed", "error", err) + logger.Error(nil, "done split history failed") + jsonhttp.InternalServerError(w, "done split history failed") + return + } + err = putter.Done(granteeref) if err != nil { logger.Debug("done split grantees failed", "error", err) @@ -305,19 +320,13 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) return } - err = putter.Done(historyref) - if err != nil { - logger.Debug("done split history failed", "error", err) - logger.Error(nil, "done split history failed") - jsonhttp.InternalServerError(w, "done split history failed") - return - } jsonhttp.OK(w, GranteesPatchResponse{ - Reference: granteeref, + Reference: encryptedglref, HistoryReference: historyref, }) } +// TODO: actCreateGranteesHandler doc. func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("acthandler").Build() @@ -402,7 +411,7 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques return } - granteeref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, convertToPointerSlice(list), nil) + granteeref, encryptedglref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, convertToPointerSlice(list), nil) if err != nil { logger.Debug("failed to update grantee list", "error", err) logger.Error(nil, "failed to update grantee list") @@ -410,14 +419,6 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques return } - err = putter.Done(granteeref) - if err != nil { - logger.Debug("done split grantees failed", "error", err) - logger.Error(nil, "done split grantees failed") - jsonhttp.InternalServerError(w, "done split grantees failed") - return - } - err = putter.Done(historyref) if err != nil { logger.Debug("done split history failed", "error", err) @@ -426,8 +427,16 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques return } + err = putter.Done(granteeref) + if err != nil { + logger.Debug("done split grantees failed", "error", err) + logger.Error(nil, "done split grantees failed") + jsonhttp.InternalServerError(w, "done split grantees failed") + return + } + jsonhttp.Created(w, GranteesPostResponse{ - Reference: granteeref, + Reference: encryptedglref, HistoryReference: historyref, }) } diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index 4405076b4c8..dd50f0cf749 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -821,24 +821,48 @@ func TestDacGrantees(t *testing.T) { }) ) t.Run("get-grantees", func(t *testing.T) { + var ( + publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey) + publisher = hex.EncodeToString(publicKeyBytes) + ) + clientwihtpublisher, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String()), mockdac.WithPublisher(publisher)), + }) expected := []string{ "03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2", "03c712a7e29bc792ac8d8ae49793d28d5bda27ed70f0d90697b2fb456c0a168bd2", "032541acf966823bae26c2c16a7102e728ade3e2e29c11a8a17b29d8eb2bd19302", } - jsonhttptest.Request(t, client, http.MethodGet, "/grantee/"+addr.String(), http.StatusOK, + jsonhttptest.Request(t, clientwihtpublisher, 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, + t.Run("get-grantees-unauthorized", func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodGet, "/grantee/fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396", http.StatusUnauthorized, jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: "Unauthorized", - Code: http.StatusBadRequest, + Message: "granteelist not found", + Code: http.StatusUnauthorized, }), ) }) + t.Run("get-grantees-invalid-address", func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodGet, "/grantee/asd", http.StatusBadRequest, + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('s').Error(), + }, + }}), + ) + }) t.Run("add-revoke-grantees", func(t *testing.T) { body := api.GranteesPatchRequest{ Addlist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index b3b808e2b74..33b8ba819ba 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -11,6 +11,8 @@ import ( ) var hashFunc = sha3.NewLegacyKeccak256 +var oneByteArray = []byte{1} +var zeroByteArray = []byte{0} // Read-only interface for the ACT type Decryptor interface { @@ -50,15 +52,20 @@ func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, pu return swarm.ZeroAddress, err } refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) - encryptedRef, _ := refCipher.Encrypt(ref.Bytes()) + encryptedRef, err := refCipher.Encrypt(ref.Bytes()) + if err != nil { + return swarm.ZeroAddress, err + } return swarm.NewAddress(encryptedRef), nil } // Adds a new grantee to the ACT func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) error { - var accessKey encryption.Key - var err error // Declare the "err" variable + var ( + accessKey encryption.Key + err error + ) if accessKeyPointer == nil { // Get previously generated access key @@ -109,9 +116,6 @@ func (al *ActLogic) getAccessKey(ctx context.Context, storage kvs.KeyValueStore, return accessKeyDecryptionCipher.Decrypt(encryptedAK) } -var oneByteArray = []byte{1} -var zeroByteArray = []byte{0} - // Generate lookup key and access key decryption key for a given public key func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { return al.Session.Key(publicKey, [][]byte{zeroByteArray, oneByteArray}) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 003d897a4ca..c8f5851d5d0 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -6,6 +6,7 @@ import ( "io" "time" + encryption "github.com/ethersphere/bee/v2/pkg/encryption" "github.com/ethersphere/bee/v2/pkg/file/loadsave" "github.com/ethersphere/bee/v2/pkg/file/pipeline" "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" @@ -18,16 +19,17 @@ import ( const granteeListEncrypt = true type GranteeManager interface { - HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) - GetGrantees(ctx context.Context, getter storage.Getter, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) + // TODO: doc + HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, error) + // GetGrantees returns the list of grantees for the given publisher. + // The list is accessible only by the publisher. + GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) } -// TODO: add granteeList ref to history metadata to solve inconsistency type Controller interface { GranteeManager // DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp. DownloadHandler(ctx context.Context, getter storage.Getter, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) - // TODO: history encryption // UploadHandler encrypts the reference and stores it in the history as the latest update. UploadHandler(ctx context.Context, getter storage.Getter, putter storage.Putter, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) io.Closer @@ -56,13 +58,12 @@ func (c *controller) DownloadHandler( if err != nil { return swarm.ZeroAddress, err } - // TODO: hanlde granteelist ref in mtdt - kvs, err := kvs.NewReference(ls, entry.Reference()) + act, err := kvs.NewReference(ls, entry.Reference()) if err != nil { return swarm.ZeroAddress, err } - return c.accessLogic.DecryptRef(ctx, kvs, encryptedRef, publisher) + return c.accessLogic.DecryptRef(ctx, act, encryptedRef, publisher) } func (c *controller) UploadHandler( @@ -97,7 +98,6 @@ func (c *controller) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - // TODO: pass granteelist ref as mtdt err = history.Add(ctx, actRef, &now, nil) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err @@ -111,7 +111,6 @@ func (c *controller) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - // TODO: hanlde granteelist ref in mtdt entry, err := history.Lookup(ctx, now) actRef = entry.Reference() if err != nil { @@ -127,69 +126,81 @@ func (c *controller) UploadHandler( return actRef, historyRef, encryptedRef, err } -func NewController(ctx context.Context, accessLogic ActLogic) Controller { +func NewController(accessLogic ActLogic) Controller { return &controller{ accessLogic: accessLogic, } } +// TODO: is an empty addlist && removelist check necessary ? func (c *controller) HandleGrantees( ctx context.Context, getter storage.Getter, putter storage.Putter, - granteeref swarm.Address, + encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey, -) (swarm.Address, swarm.Address, error) { +) (swarm.Address, swarm.Address, swarm.Address, error) { var ( - err error - h History - act kvs.KeyValueStore - ls = loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, false, redundancy.NONE)) - gls = loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) + err error + h History + act kvs.KeyValueStore + granteeref swarm.Address + ls = loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + gls = loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) ) if !historyref.IsZero() { h, err = NewHistoryReference(ls, historyref) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } entry, err := h.Lookup(ctx, time.Now().Unix()) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } actref := entry.Reference() act, err = kvs.NewReference(ls, actref) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } else { h, err = NewHistory(ls) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } act, err = kvs.New(ls) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } var gl GranteeList - if granteeref.IsZero() { - gl = NewGranteeList(gls) + if encryptedglref.IsZero() { + gl, err = NewGranteeList(gls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } else { - gl = NewGranteeListReference(gls, granteeref) + granteeref, err = c.decryptRefForPublisher(publisher, encryptedglref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + gl, err = NewGranteeListReference(gls, granteeref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } err = gl.Add(addList) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } if len(removeList) != 0 { err = gl.Remove(removeList) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } @@ -198,7 +209,7 @@ func (c *controller) HandleGrantees( if len(removeList) != 0 || granteeref.IsZero() { err = c.accessLogic.AddPublisher(ctx, act, publisher) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } granteesToAdd = gl.Get() } else { @@ -208,37 +219,78 @@ func (c *controller) HandleGrantees( for _, grantee := range granteesToAdd { err := c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } actref, err := act.Save(ctx) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - err = h.Add(ctx, actref, nil, nil) + glref, err := gl.Save(ctx) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - href, err := h.Store(ctx) + eglref, err := c.encryptRefForPublisher(publisher, glref) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - glref, err := gl.Save(ctx) + mtdt := map[string]string{"encryptedglref": eglref.String()} + err = h.Add(ctx, actref, nil, &mtdt) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + href, err := h.Store(ctx) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - return glref, href, nil + + return glref, eglref, href, nil } -func (c *controller) GetGrantees(ctx context.Context, getter storage.Getter, granteeRef swarm.Address) ([]*ecdsa.PublicKey, error) { +func (c *controller) GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { ls := loadsave.NewReadonly(getter) - gl := NewGranteeListReference(ls, granteeRef) + granteeRef, err := c.decryptRefForPublisher(publisher, encryptedglref) + if err != nil { + return nil, err + } + gl, err := NewGranteeListReference(ls, granteeRef) + if err != nil { + return nil, err + } return gl.Get(), nil } +func (c *controller) encryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { + keys, err := c.accessLogic.Session.Key(publisherPubKey, [][]byte{oneByteArray}) + if err != nil { + return swarm.ZeroAddress, err + } + refCipher := encryption.New(keys[0], 0, uint32(0), hashFunc) + encryptedRef, err := refCipher.Encrypt(ref.Bytes()) + if err != nil { + return swarm.ZeroAddress, err + } + + return swarm.NewAddress(encryptedRef), nil +} + +func (c *controller) decryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, encryptedRef swarm.Address) (swarm.Address, error) { + keys, err := c.accessLogic.Session.Key(publisherPubKey, [][]byte{oneByteArray}) + if err != nil { + return swarm.ZeroAddress, err + } + refCipher := encryption.New(keys[0], 0, uint32(0), hashFunc) + ref, err := refCipher.Decrypt(encryptedRef.Bytes()) + if err != nil { + return swarm.ZeroAddress, err + } + + return swarm.NewAddress(ref), nil +} + func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { return func() pipeline.Interface { return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 3d5b4a5b850..e12a6acc624 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -3,20 +3,23 @@ package dynamicaccess_test import ( "context" "crypto/ecdsa" + "reflect" "testing" "time" "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + encryption "github.com/ethersphere/bee/v2/pkg/encryption" "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/kvs" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" + "golang.org/x/crypto/sha3" ) func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) { h, err := dynamicaccess.NewHistory(ls) if err != nil { - return swarm.ZeroAddress, nil + return swarm.ZeroAddress, err } pk1 := getPrivKey(1) pk2 := getPrivKey(2) @@ -47,7 +50,7 @@ func TestController_NewUpload(t *testing.T) { publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) - c := dynamicaccess.NewController(ctx, al) + c := dynamicaccess.NewController(al) ref := swarm.RandAddress(t) _, hRef, encRef, err := c.UploadHandler(ctx, mockStorer.ChunkStore(), mockStorer.Cache(), ref, &publisher.PublicKey, swarm.ZeroAddress) @@ -68,7 +71,7 @@ func TestController_PublisherDownload(t *testing.T) { publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) - c := dynamicaccess.NewController(ctx, al) + c := dynamicaccess.NewController(al) ls := createLs() ref := swarm.RandAddress(t) href, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) @@ -94,7 +97,7 @@ func TestController_GranteeDownload(t *testing.T) { diffieHellman := dynamicaccess.NewDefaultSession(grantee) al := dynamicaccess.NewLogic(diffieHellman) ls := createLs() - c := dynamicaccess.NewController(ctx, al) + c := dynamicaccess.NewController(al) ref := swarm.RandAddress(t) href, err := getHistoryFixture(ctx, ls, publisherAL, &publisher.PublicKey) h, err := dynamicaccess.NewHistoryReference(ls, href) @@ -115,10 +118,12 @@ func TestController_HandleGrantees(t *testing.T) { publisher := getPrivKey(1) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) + keys, _ := al.Session.Key(&publisher.PublicKey, [][]byte{{1}}) + refCipher := encryption.New(keys[0], 0, uint32(0), sha3.NewLegacyKeccak256) ls := createLs() getter := mockStorer.ChunkStore() putter := mockStorer.Cache() - c := dynamicaccess.NewController(ctx, al) + c := dynamicaccess.NewController(al) href, _ := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) grantee1 := getPrivKey(0) @@ -126,37 +131,38 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add to new list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) - gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) }) t.Run("add to existing list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, eglref, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} - granteeRef, _, err = c.HandleGrantees(ctx, getter, putter, granteeRef, href, &publisher.PublicKey, addList, nil) - gl = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + granteeRef, _, _, err = c.HandleGrantees(ctx, getter, putter, eglref, href, &publisher.PublicKey, addList, nil) + gl, err = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) }) t.Run("add and revoke", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} revokeList := []*ecdsa.PublicKey{&grantee1.PublicKey} - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) granteeRef, err := gl.Save(ctx) + eglref, _ := refCipher.Encrypt(granteeRef.Bytes()) - granteeRef, _, err = c.HandleGrantees(ctx, getter, putter, granteeRef, href, &publisher.PublicKey, addList, revokeList) - gl = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + granteeRef, _, _, err = c.HandleGrantees(ctx, getter, putter, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) + gl, err = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) @@ -164,19 +170,54 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add twice", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} - granteeRef, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - granteeRef, _, err = c.HandleGrantees(ctx, getter, putter, granteeRef, href, &publisher.PublicKey, addList, nil) - gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + granteeRef, eglref, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, err = c.HandleGrantees(ctx, getter, putter, eglref, href, &publisher.PublicKey, addList, nil) + gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) }) t.Run("revoke non-existing", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - gl := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + granteeRef, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) }) } + +func TestController_GetGrantees(t *testing.T) { + ctx := context.Background() + publisher := getPrivKey(1) + caller := getPrivKey(0) + grantee := getPrivKey(2) + diffieHellman1 := dynamicaccess.NewDefaultSession(publisher) + diffieHellman2 := dynamicaccess.NewDefaultSession(caller) + al1 := dynamicaccess.NewLogic(diffieHellman1) + al2 := dynamicaccess.NewLogic(diffieHellman2) + ls := createLs() + getter := mockStorer.ChunkStore() + putter := mockStorer.Cache() + c1 := dynamicaccess.NewController(al1) + c2 := dynamicaccess.NewController(al2) + + t.Run("get by publisher", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + granteeRef, eglRef, _, err := c1.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + + grantees, err := c1.GetGrantees(ctx, getter, &publisher.PublicKey, eglRef) + assert.NoError(t, err) + assert.True(t, reflect.DeepEqual(grantees, addList)) + + gl, _ := dynamicaccess.NewGranteeListReference(ls, granteeRef) + assert.True(t, reflect.DeepEqual(gl.Get(), addList)) + }) + t.Run("get by non-publisher", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + _, eglRef, _, err := c1.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + grantees, err := c2.GetGrantees(ctx, getter, &publisher.PublicKey, eglRef) + assert.Error(t, err) + assert.Nil(t, grantees) + }) +} diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index b0a1a3799cd..02cd8099021 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -15,10 +15,15 @@ const ( publicKeyLen = 65 ) +// GranteeList manages a list of public keys. type GranteeList interface { + // Add adds a list of public keys to the grantee list. It filters out duplicates. Add(addList []*ecdsa.PublicKey) error + // Remove removes a list of public keys from the grantee list, if there is any. Remove(removeList []*ecdsa.PublicKey) error + // Get simply returns the list of public keys. Get() []*ecdsa.PublicKey + // Save saves the grantee list to the underlying storage and returns the reference. Save(ctx context.Context) (swarm.Address, error) } @@ -37,7 +42,26 @@ func (g *GranteeListStruct) Add(addList []*ecdsa.PublicKey) error { if len(addList) == 0 { return fmt.Errorf("no public key provided") } - g.grantees = append(g.grantees, addList...) + filteredList := make([]*ecdsa.PublicKey, 0, len(addList)) + for _, addkey := range addList { + add := true + for _, granteekey := range g.grantees { + if granteekey.Equal(addkey) { + add = false + break + } + } + for _, filteredkey := range filteredList { + if filteredkey.Equal(addkey) { + add = false + break + } + } + if add { + filteredList = append(filteredList, addkey) + } + } + g.grantees = append(g.grantees, filteredList...) return nil } @@ -75,24 +99,24 @@ func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error { return nil } -func NewGranteeList(ls file.LoadSaver) GranteeList { +func NewGranteeList(ls file.LoadSaver) (GranteeList, error) { return &GranteeListStruct{ grantees: []*ecdsa.PublicKey{}, loadSave: ls, - } + }, nil } -func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) GranteeList { +func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) (GranteeList, error) { data, err := ls.Load(context.Background(), reference.Bytes()) if err != nil { - return nil + return nil, err } grantees := deserialize(data) return &GranteeListStruct{ grantees: grantees, loadSave: ls, - } + }, nil } func serialize(publicKeys []*ecdsa.PublicKey) []byte { diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 8782a96bc45..c4ce58ac8aa 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -15,6 +15,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/storage" mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" ) @@ -41,7 +42,7 @@ func generateKeyListFixture() ([]*ecdsa.PublicKey, error) { } func TestGranteeAddGet(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { t.Errorf("key generation error: %v", err) @@ -54,9 +55,10 @@ func TestGranteeAddGet(t *testing.T) { t.Run("Get should return value equal to put value", func(t *testing.T) { var ( + keys2, _ = generateKeyListFixture() addList1 []*ecdsa.PublicKey = []*ecdsa.PublicKey{keys[0]} - addList2 []*ecdsa.PublicKey = []*ecdsa.PublicKey{keys[1], keys[0]} - addList3 []*ecdsa.PublicKey = keys + addList2 []*ecdsa.PublicKey = []*ecdsa.PublicKey{keys[1], keys[2]} + addList3 []*ecdsa.PublicKey = keys2 ) testCases := []struct { name string @@ -66,6 +68,10 @@ func TestGranteeAddGet(t *testing.T) { name: "Test list = 1", list: addList1, }, + { + name: "Test list = duplicate1", + list: addList1, + }, { name: "Test list = 2", list: addList2, @@ -88,7 +94,9 @@ func TestGranteeAddGet(t *testing.T) { assert.Error(t, err) } else { assert.NoError(t, err) - expList = append(expList, tc.list...) + if tc.name != "Test list = duplicate1" { + expList = append(expList, tc.list...) + } retVal := gl.Get() assert.Equal(t, expList, retVal) } @@ -98,7 +106,7 @@ func TestGranteeAddGet(t *testing.T) { } func TestGranteeRemove(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { t.Errorf("key generation error: %v", err) @@ -150,13 +158,18 @@ func TestGranteeSave(t *testing.T) { if err != nil { t.Errorf("key generation error: %v", err) } + t.Run("Create grantee list with invalid reference, expect error", func(t *testing.T) { + gl, err := dynamicaccess.NewGranteeListReference(createLs(), swarm.RandAddress(t)) + assert.Error(t, err) + assert.Nil(t, gl) + }) t.Run("Save empty grantee list return NO error", func(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) _, err := gl.Save(ctx) assert.NoError(t, err) }) t.Run("Save not empty grantee list return valid swarm address", func(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) err = gl.Add(keys) ref, err := gl.Save(ctx) assert.NoError(t, err) @@ -164,7 +177,7 @@ func TestGranteeSave(t *testing.T) { }) t.Run("Save grantee list with one item, no error, pre-save value exist", func(t *testing.T) { ls := createLs() - gl1 := dynamicaccess.NewGranteeList(ls) + gl1, _ := dynamicaccess.NewGranteeList(ls) err := gl1.Add(keys) assert.NoError(t, err) @@ -172,32 +185,33 @@ func TestGranteeSave(t *testing.T) { ref, err := gl1.Save(ctx) assert.NoError(t, err) - gl2 := dynamicaccess.NewGranteeListReference(ls, ref) + gl2, _ := dynamicaccess.NewGranteeListReference(ls, ref) val := gl2.Get() assert.NoError(t, err) assert.Equal(t, keys, val) }) t.Run("Save grantee list and add one item, no error, after-save value exist", func(t *testing.T) { ls := createLs() + keys2, _ := generateKeyListFixture() - gl1 := dynamicaccess.NewGranteeList(ls) + gl1, _ := dynamicaccess.NewGranteeList(ls) err := gl1.Add(keys) assert.NoError(t, err) ref, err := gl1.Save(ctx) assert.NoError(t, err) - gl2 := dynamicaccess.NewGranteeListReference(ls, ref) - err = gl2.Add(keys) + gl2, _ := dynamicaccess.NewGranteeListReference(ls, ref) + err = gl2.Add(keys2) assert.NoError(t, err) val := gl2.Get() - assert.Equal(t, append(keys, keys...), val) + assert.Equal(t, append(keys, keys2...), val) }) } func TestGranteeRemoveTwo(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { t.Errorf("key generation error: %v", err) diff --git a/pkg/dynamicaccess/mock/service.go b/pkg/dynamicaccess/mock/controller.go similarity index 93% rename from pkg/dynamicaccess/mock/service.go rename to pkg/dynamicaccess/mock/controller.go index 15f26ac6f00..241e22fa931 100644 --- a/pkg/dynamicaccess/mock/service.go +++ b/pkg/dynamicaccess/mock/controller.go @@ -126,7 +126,6 @@ func (m *mockDacService) UploadHandler(ctx context.Context, getter storage.Gette } } else { h, _ = dynamicaccess.NewHistory(m.ls) - // TODO: pass granteelist ref as mtdt h.Add(ctx, kvsRef, &now, nil) historyRef, _ = h.Store(ctx) m.historyMap[historyRef.String()] = h @@ -141,18 +140,23 @@ func (m *mockDacService) Close() error { return nil } -func (m *mockDacService) HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { +func (m *mockDacService) HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") - return glRef, historyRef, nil + eglRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") + return glRef, eglRef, historyRef, nil } -func (m *mockDacService) GetGrantees(ctx context.Context, getter storage.Getter, granteeref swarm.Address) ([]*ecdsa.PublicKey, error) { + +func (m *mockDacService) GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, granteeref swarm.Address) ([]*ecdsa.PublicKey, error) { + if m.publisher == "" { + return nil, fmt.Errorf("granteelist not found") + } keys := []string{ "a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa", "b786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfb", "c786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfc", } - var pubkeys []*ecdsa.PublicKey + pubkeys := make([]*ecdsa.PublicKey, 0, len(keys)) for i := range keys { data, err := hex.DecodeString(keys[i]) if err != nil { diff --git a/pkg/node/devnode.go b/pkg/node/devnode.go index b7ac3dfee56..275140e10a5 100644 --- a/pkg/node/devnode.go +++ b/pkg/node/devnode.go @@ -238,7 +238,7 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { session := dynamicaccess.NewDefaultSession(mockKey) actLogic := dynamicaccess.NewLogic(session) - dac := dynamicaccess.NewController(context.Background(), actLogic) + dac := dynamicaccess.NewController(actLogic) b.dacCloser = dac pssService := pss.New(mockKey, logger) diff --git a/pkg/node/node.go b/pkg/node/node.go index b48e97ddafc..4002693ff2e 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -776,7 +776,7 @@ func NewBee( evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) } actLogic := dynamicaccess.NewLogic(session) - dac := dynamicaccess.NewController(ctx, actLogic) + dac := dynamicaccess.NewController(actLogic) b.dacCloser = dac var ( From e49e4a2531388962db8641826c50efd2959c2288 Mon Sep 17 00:00:00 2001 From: Kexort Date: Mon, 29 Apr 2024 08:34:29 +0200 Subject: [PATCH 07/14] Fix nil pointer dereference panic --- pkg/api/dynamicaccess.go | 6 +++--- pkg/api/dynamicaccess_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 505e33ee950..96c1f542bbf 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -179,7 +179,7 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) if err != nil { logger.Debug("could not get grantees", "error", err) logger.Error(nil, "could not get grantees") - jsonhttp.Unauthorized(w, "granteelist not found") + jsonhttp.NotFound(w, "granteelist not found") return } granteeSlice := make([]string, len(grantees)) @@ -366,7 +366,7 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques } } - list := make([]ecdsa.PublicKey, len(gpr.GranteeList)) + list := make([]ecdsa.PublicKey, 0, len(gpr.GranteeList)) for _, g := range gpr.GranteeList { h, _ := hex.DecodeString(g) k, _ := btcec.ParsePubKey(h) @@ -451,7 +451,7 @@ func convertToPointerSlice(slice []ecdsa.PublicKey) []*ecdsa.PublicKey { } func parseKeys(list []string) ([]ecdsa.PublicKey, error) { - parsedList := make([]ecdsa.PublicKey, len(list)) + parsedList := make([]ecdsa.PublicKey, 0, len(list)) for _, g := range list { h, err := hex.DecodeString(g) if err != nil { diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index dd50f0cf749..3e316b6bf64 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -846,7 +846,7 @@ func TestDacGrantees(t *testing.T) { jsonhttptest.Request(t, client, http.MethodGet, "/grantee/fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396", http.StatusUnauthorized, jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ Message: "granteelist not found", - Code: http.StatusUnauthorized, + Code: http.StatusNotFound, }), ) }) From d0f9dddcf4e8ee949bcc5e0b66fe90abb5dacf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= Date: Mon, 29 Apr 2024 13:10:15 +0200 Subject: [PATCH 08/14] CHG: put actref in handlegrantees; Add: pin, tag,deferred headers --- pkg/api/dynamicaccess.go | 135 ++++++++++++++++----------- pkg/api/dynamicaccess_test.go | 2 + pkg/dynamicaccess/controller.go | 46 ++++----- pkg/dynamicaccess/controller_test.go | 18 ++-- pkg/dynamicaccess/mock/controller.go | 5 +- 5 files changed, 116 insertions(+), 90 deletions(-) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 96c1f542bbf..a8ca979cbc8 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -54,8 +54,8 @@ type GranteesPostResponse struct { HistoryReference swarm.Address `json:"historyref"` } type GranteesPatch struct { - Addlist []ecdsa.PublicKey - Revokelist []ecdsa.PublicKey + Addlist []*ecdsa.PublicKey + Revokelist []*ecdsa.PublicKey } // actDecryptionHandler is a middleware that looks up and decrypts the given address, @@ -139,13 +139,6 @@ func (s *Service) actEncryptionHandler( return swarm.ZeroAddress, err } } - // TODO: probably does not need to store the eref, just return it - err = putter.Done(encryptedReference) - if err != nil { - logger.Debug("done split encrypted reference failed", "error", err) - logger.Error(nil, "done split encrypted reference failed") - return swarm.ZeroAddress, err - } w.Header().Set(SwarmActHistoryAddressHeader, historyReference.String()) return encryptedReference, nil @@ -182,7 +175,7 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) jsonhttp.NotFound(w, "granteelist not found") return } - granteeSlice := make([]string, len(grantees)) + granteeSlice := make([]string, 0, len(grantees)) for i, grantee := range grantees { granteeSlice[i] = hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(grantee)) } @@ -209,6 +202,9 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) headers := struct { BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address" validate:"required"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { @@ -216,6 +212,27 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) return } + var ( + tag uint64 + err error + deferred = defaultUploadMethod(headers.Deferred) + ) + + if deferred || headers.Pin { + tag, err = s.getOrCreateSessionID(headers.SwarmTag) + if err != nil { + logger.Debug("get or create tag failed", "error", err) + logger.Error(nil, "get or create tag failed") + switch { + case errors.Is(err, storage.ErrNotFound): + jsonhttp.NotFound(w, "tag not found") + default: + jsonhttp.InternalServerError(w, "cannot get or create tag") + } + return + } + } + body, err := io.ReadAll(r.Body) if err != nil { if jsonhttp.HandleBodyReadError(err, w) { @@ -257,19 +274,6 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) } grantees.Revokelist = append(grantees.Revokelist, paresRevokelist...) - tag, err := s.getOrCreateSessionID(0) - if err != nil { - logger.Debug("get or create tag failed", "error", err) - logger.Error(nil, "get or create tag failed") - switch { - case errors.Is(err, storage.ErrNotFound): - jsonhttp.NotFound(w, "tag not found") - default: - jsonhttp.InternalServerError(w, "cannot get or create tag") - } - return - } - ctx := r.Context() putter, err := s.newStamperPutter(ctx, putterOptions{ BatchID: headers.BatchID, @@ -296,7 +300,7 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) } granteeref := paths.GranteesAddress - granteeref, encryptedglref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, granteeref, *headers.HistoryAddress, &s.publicKey, convertToPointerSlice(grantees.Addlist), convertToPointerSlice(grantees.Revokelist)) + granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, granteeref, *headers.HistoryAddress, &s.publicKey, grantees.Addlist, grantees.Revokelist) if err != nil { logger.Debug("failed to update grantee list", "error", err) logger.Error(nil, "failed to update grantee list") @@ -304,6 +308,14 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) return } + err = putter.Done(actref) + if err != nil { + logger.Debug("done split act failed", "error", err) + logger.Error(nil, "done split act failed") + jsonhttp.InternalServerError(w, "done split act failed") + return + } + err = putter.Done(historyref) if err != nil { logger.Debug("done split history failed", "error", err) @@ -337,13 +349,37 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques } headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) return } + var ( + tag uint64 + err error + deferred = defaultUploadMethod(headers.Deferred) + ) + + if deferred || headers.Pin { + tag, err = s.getOrCreateSessionID(headers.SwarmTag) + if err != nil { + logger.Debug("get or create tag failed", "error", err) + logger.Error(nil, "get or create tag failed") + switch { + case errors.Is(err, storage.ErrNotFound): + jsonhttp.NotFound(w, "tag not found") + default: + jsonhttp.InternalServerError(w, "cannot get or create tag") + } + return + } + } + body, err := io.ReadAll(r.Body) if err != nil { if jsonhttp.HandleBodyReadError(err, w) { @@ -366,27 +402,15 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques } } - list := make([]ecdsa.PublicKey, 0, len(gpr.GranteeList)) - for _, g := range gpr.GranteeList { - h, _ := hex.DecodeString(g) - k, _ := btcec.ParsePubKey(h) - list = append(list, *k.ToECDSA()) - } - tag, err := s.getOrCreateSessionID(0) + list, err := parseKeys(gpr.GranteeList) if err != nil { - logger.Debug("get or create tag failed", "error", err) - logger.Error(nil, "get or create tag failed") - switch { - case errors.Is(err, storage.ErrNotFound): - jsonhttp.NotFound(w, "tag not found") - default: - jsonhttp.InternalServerError(w, "cannot get or create tag") - } + logger.Debug("create list key parse failed", "error", err) + logger.Error(nil, "create list key parse failed") + jsonhttp.InternalServerError(w, "error create list key parsing") return } ctx := r.Context() - // TODO: pin and deferred headers putter, err := s.newStamperPutter(ctx, putterOptions{ BatchID: headers.BatchID, TagID: tag, @@ -411,7 +435,7 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques return } - granteeref, encryptedglref, historyref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, convertToPointerSlice(list), nil) + granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, list, nil) if err != nil { logger.Debug("failed to update grantee list", "error", err) logger.Error(nil, "failed to update grantee list") @@ -419,6 +443,14 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques return } + err = putter.Done(actref) + if err != nil { + logger.Debug("done split act failed", "error", err) + logger.Error(nil, "done split act failed") + jsonhttp.InternalServerError(w, "done split act failed") + return + } + err = putter.Done(historyref) if err != nil { logger.Debug("done split history failed", "error", err) @@ -441,27 +473,18 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques }) } -func convertToPointerSlice(slice []ecdsa.PublicKey) []*ecdsa.PublicKey { - pointerSlice := make([]*ecdsa.PublicKey, len(slice)) - for i, key := range slice { - tempKey := key - pointerSlice[i] = &tempKey - } - return pointerSlice -} - -func parseKeys(list []string) ([]ecdsa.PublicKey, error) { - parsedList := make([]ecdsa.PublicKey, 0, len(list)) +func parseKeys(list []string) ([]*ecdsa.PublicKey, error) { + parsedList := make([]*ecdsa.PublicKey, 0, len(list)) for _, g := range list { h, err := hex.DecodeString(g) if err != nil { - return []ecdsa.PublicKey{}, err + return []*ecdsa.PublicKey{}, err } k, err := btcec.ParsePubKey(h) if err != nil { - return []ecdsa.PublicKey{}, err + return []*ecdsa.PublicKey{}, err } - parsedList = append(parsedList, *k.ToECDSA()) + parsedList = append(parsedList, k.ToECDSA()) } return parsedList, nil diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index 3e316b6bf64..4b503f9ffcb 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -62,6 +62,7 @@ func prepareHistoryFixture(storer api.Storer) (dynamicaccess.History, swarm.Addr return h, ref } +// TODO: test tag, pin, deferred, stamp // TODO: feed test // nolint:paralleltest,tparallel // TestDacWithoutActHeader [positive tests]: @@ -387,6 +388,7 @@ func TestDacHistory(t *testing.T) { fileName = "sample.html" now = time.Now().Unix() ) + fmt.Printf("bagoy now: %d\n", now) t.Run("empty-history-upload-then-download-and-check-data", func(t *testing.T) { client, _, _, _ := newTestServer(t, testServerOptions{ diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index c8f5851d5d0..c47f78d8ef1 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -20,7 +20,7 @@ const granteeListEncrypt = true type GranteeManager interface { // TODO: doc - HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, error) + HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) // GetGrantees returns the list of grantees for the given publisher. // The list is accessible only by the publisher. GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) @@ -132,7 +132,6 @@ func NewController(accessLogic ActLogic) Controller { } } -// TODO: is an empty addlist && removelist check necessary ? func (c *controller) HandleGrantees( ctx context.Context, getter storage.Getter, @@ -142,7 +141,7 @@ func (c *controller) HandleGrantees( publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey, -) (swarm.Address, swarm.Address, swarm.Address, error) { +) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { var ( err error h History @@ -154,26 +153,25 @@ func (c *controller) HandleGrantees( if !historyref.IsZero() { h, err = NewHistoryReference(ls, historyref) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } entry, err := h.Lookup(ctx, time.Now().Unix()) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } actref := entry.Reference() act, err = kvs.NewReference(ls, actref) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } else { h, err = NewHistory(ls) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } act, err = kvs.New(ls) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } @@ -181,35 +179,36 @@ func (c *controller) HandleGrantees( if encryptedglref.IsZero() { gl, err = NewGranteeList(gls) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } else { granteeref, err = c.decryptRefForPublisher(publisher, encryptedglref) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } + gl, err = NewGranteeListReference(gls, granteeref) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } err = gl.Add(addList) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } if len(removeList) != 0 { err = gl.Remove(removeList) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } var granteesToAdd []*ecdsa.PublicKey // generate new access key and new act - if len(removeList) != 0 || granteeref.IsZero() { + if len(removeList) != 0 || encryptedglref.IsZero() { err = c.accessLogic.AddPublisher(ctx, act, publisher) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } granteesToAdd = gl.Get() } else { @@ -219,35 +218,36 @@ func (c *controller) HandleGrantees( for _, grantee := range granteesToAdd { err := c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } actref, err := act.Save(ctx) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } glref, err := gl.Save(ctx) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } + eglref, err := c.encryptRefForPublisher(publisher, glref) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } mtdt := map[string]string{"encryptedglref": eglref.String()} err = h.Add(ctx, actref, nil, &mtdt) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } href, err := h.Store(ctx) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - return glref, eglref, href, nil + return glref, eglref, href, actref, nil } func (c *controller) GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index e12a6acc624..5298ac8e7e7 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -131,7 +131,7 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add to new list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) @@ -140,7 +140,7 @@ func TestController_HandleGrantees(t *testing.T) { }) t.Run("add to existing list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, eglref, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) @@ -148,7 +148,7 @@ func TestController_HandleGrantees(t *testing.T) { assert.Len(t, gl.Get(), 1) addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} - granteeRef, _, _, err = c.HandleGrantees(ctx, getter, putter, eglref, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err = c.HandleGrantees(ctx, getter, putter, eglref, href, &publisher.PublicKey, addList, nil) gl, err = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) @@ -161,7 +161,7 @@ func TestController_HandleGrantees(t *testing.T) { granteeRef, err := gl.Save(ctx) eglref, _ := refCipher.Encrypt(granteeRef.Bytes()) - granteeRef, _, _, err = c.HandleGrantees(ctx, getter, putter, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) + granteeRef, _, _, _, err = c.HandleGrantees(ctx, getter, putter, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) gl, err = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -170,8 +170,8 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add twice", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} - granteeRef, eglref, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - granteeRef, _, _, err = c.HandleGrantees(ctx, getter, putter, eglref, href, &publisher.PublicKey, addList, nil) + granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err = c.HandleGrantees(ctx, getter, putter, eglref, href, &publisher.PublicKey, addList, nil) gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -179,7 +179,7 @@ func TestController_HandleGrantees(t *testing.T) { }) t.Run("revoke non-existing", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -204,7 +204,7 @@ func TestController_GetGrantees(t *testing.T) { t.Run("get by publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, eglRef, _, err := c1.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, eglRef, _, _, err := c1.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) grantees, err := c1.GetGrantees(ctx, getter, &publisher.PublicKey, eglRef) assert.NoError(t, err) @@ -215,7 +215,7 @@ func TestController_GetGrantees(t *testing.T) { }) t.Run("get by non-publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - _, eglRef, _, err := c1.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + _, eglRef, _, _, err := c1.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) grantees, err := c2.GetGrantees(ctx, getter, &publisher.PublicKey, eglRef) assert.Error(t, err) assert.Nil(t, grantees) diff --git a/pkg/dynamicaccess/mock/controller.go b/pkg/dynamicaccess/mock/controller.go index 241e22fa931..10f4c2823a1 100644 --- a/pkg/dynamicaccess/mock/controller.go +++ b/pkg/dynamicaccess/mock/controller.go @@ -140,11 +140,12 @@ func (m *mockDacService) Close() error { return nil } -func (m *mockDacService) HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockDacService) HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") eglRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") - return glRef, eglRef, historyRef, nil + actref, _ := swarm.ParseHexAddress("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") + return glRef, eglRef, historyRef, actref, nil } func (m *mockDacService) GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, granteeref swarm.Address) ([]*ecdsa.PublicKey, error) { From 5ae2d9df23f293feef7955ac208c062c3da7a6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= Date: Mon, 6 May 2024 12:59:10 +0200 Subject: [PATCH 09/14] CHG: pass loadsave to handlers; check if history address is nil --- pkg/api/bytes.go | 2 +- pkg/api/bzz.go | 2 +- pkg/api/chunk.go | 2 +- pkg/api/dirs.go | 2 +- pkg/api/dynamicaccess.go | 54 ++++++++++++++++++++-------- pkg/api/feed.go | 2 +- pkg/api/soc.go | 2 +- pkg/dynamicaccess/controller.go | 28 ++++++--------- pkg/dynamicaccess/controller_test.go | 48 ++++++++++++------------- pkg/dynamicaccess/mock/controller.go | 8 ++--- 10 files changed, 83 insertions(+), 67 deletions(-) diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index a2d65cae1ba..2afb1db99c0 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -118,7 +118,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := reference if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, reference, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index 7f322be0720..b49bc89baea 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -266,7 +266,7 @@ func (s *Service) fileUploadHandler( encryptedReference := manifestReference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, manifestReference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, manifestReference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index 9c462b1a558..21daa0d0f57 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -143,7 +143,7 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := chunk.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, chunk.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, chunk.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dirs.go b/pkg/api/dirs.go index e871eee2e8b..f187fbde01e 100644 --- a/pkg/api/dirs.go +++ b/pkg/api/dirs.go @@ -102,7 +102,7 @@ func (s *Service) dirUploadHandler( encryptedReference := reference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, reference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index a8ca979cbc8..2ba951c5d67 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -11,6 +11,8 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/jsonhttp" "github.com/ethersphere/bee/v2/pkg/postage" storage "github.com/ethersphere/bee/v2/pkg/storage" @@ -21,6 +23,8 @@ import ( type addressKey struct{} +const granteeListEncrypt = true + // getAddressFromContext is a helper function to extract the address from the context func getAddressFromContext(ctx context.Context) swarm.Address { v, ok := ctx.Value(addressKey{}).(swarm.Address) @@ -53,6 +57,7 @@ type GranteesPostResponse struct { Reference swarm.Address `json:"ref"` HistoryReference swarm.Address `json:"historyref"` } + type GranteesPatch struct { Addlist []*ecdsa.PublicKey Revokelist []*ecdsa.PublicKey @@ -94,7 +99,8 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { cache = *headers.Cache } ctx := r.Context() - reference, err := s.dac.DownloadHandler(ctx, s.storer.Download(cache), paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) + ls := loadsave.NewReadonly(s.storer.Download(cache)) + reference, err := s.dac.DownloadHandler(ctx, ls, paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) if err != nil { jsonhttp.InternalServerError(w, errActDownload) return @@ -110,14 +116,14 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { func (s *Service) actEncryptionHandler( ctx context.Context, w http.ResponseWriter, - getter storage.Getter, putter storer.PutterSession, reference swarm.Address, historyRootHash swarm.Address, ) (swarm.Address, error) { logger := s.logger.WithName("act_encryption_handler").Build() publisherPublicKey := &s.publicKey - storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, getter, putter, reference, publisherPublicKey, historyRootHash) + ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, ls, reference, publisherPublicKey, historyRootHash) if err != nil { logger.Debug("act failed to encrypt reference", "error", err) logger.Error(nil, "act failed to encrypt reference") @@ -168,14 +174,15 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) cache = *headers.Cache } publisher := &s.publicKey - grantees, err := s.dac.GetGrantees(r.Context(), s.storer.Download(cache), publisher, paths.GranteesAddress) + ls := loadsave.NewReadonly(s.storer.Download(cache)) + grantees, err := s.dac.GetGrantees(r.Context(), ls, publisher, paths.GranteesAddress) if err != nil { logger.Debug("could not get grantees", "error", err) logger.Error(nil, "could not get grantees") jsonhttp.NotFound(w, "granteelist not found") return } - granteeSlice := make([]string, 0, len(grantees)) + granteeSlice := make([]string, len(grantees)) for i, grantee := range grantees { granteeSlice[i] = hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(grantee)) } @@ -212,6 +219,11 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) return } + historyAddress := swarm.ZeroAddress + if headers.HistoryAddress != nil { + historyAddress = *headers.HistoryAddress + } + var ( tag uint64 err error @@ -278,8 +290,8 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) putter, err := s.newStamperPutter(ctx, putterOptions{ BatchID: headers.BatchID, TagID: tag, - Pin: false, - Deferred: false, + Pin: headers.Pin, + Deferred: deferred, }) if err != nil { logger.Debug("putter failed", "error", err) @@ -300,7 +312,10 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) } granteeref := paths.GranteesAddress - granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, granteeref, *headers.HistoryAddress, &s.publicKey, grantees.Addlist, grantees.Revokelist) + publisher := &s.publicKey + ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + gls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) + granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, ls, gls, granteeref, historyAddress, publisher, grantees.Addlist, grantees.Revokelist) if err != nil { logger.Debug("failed to update grantee list", "error", err) logger.Error(nil, "failed to update grantee list") @@ -349,16 +364,22 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques } headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - SwarmTag uint64 `map:"Swarm-Tag"` - Pin bool `map:"Swarm-Pin"` - Deferred *bool `map:"Swarm-Deferred-Upload"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) return } + historyAddress := swarm.ZeroAddress + if headers.HistoryAddress != nil { + historyAddress = *headers.HistoryAddress + } + var ( tag uint64 err error @@ -414,8 +435,8 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques putter, err := s.newStamperPutter(ctx, putterOptions{ BatchID: headers.BatchID, TagID: tag, - Pin: false, - Deferred: false, + Pin: headers.Pin, + Deferred: deferred, }) if err != nil { logger.Debug("putter failed", "error", err) @@ -435,7 +456,10 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques return } - granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, s.storer.ChunkStore(), putter, swarm.ZeroAddress, swarm.ZeroAddress, &s.publicKey, list, nil) + publisher := &s.publicKey + ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + gls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) + granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, historyAddress, publisher, list, nil) if err != nil { logger.Debug("failed to update grantee list", "error", err) logger.Error(nil, "failed to update grantee list") diff --git a/pkg/api/feed.go b/pkg/api/feed.go index 3e4b8d5c10e..3d43d3d148e 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -249,7 +249,7 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { // TODO: do we want to allow feed act upload/ download? encryptedReference := ref if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, ref, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, ref, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/soc.go b/pkg/api/soc.go index cf37432ed1f..29777066b10 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -159,7 +159,7 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := sch.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), w, s.storer.ChunkStore(), putter, sch.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, sch.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index c47f78d8ef1..3764ea2d31f 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -7,7 +7,7 @@ import ( "time" encryption "github.com/ethersphere/bee/v2/pkg/encryption" - "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/file/pipeline" "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" "github.com/ethersphere/bee/v2/pkg/file/redundancy" @@ -16,22 +16,20 @@ import ( "github.com/ethersphere/bee/v2/pkg/swarm" ) -const granteeListEncrypt = true - type GranteeManager interface { // TODO: doc - HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) + HandleGrantees(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) // GetGrantees returns the list of grantees for the given publisher. // The list is accessible only by the publisher. - GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) + GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) } type Controller interface { GranteeManager // DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp. - DownloadHandler(ctx context.Context, getter storage.Getter, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) + DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) // UploadHandler encrypts the reference and stores it in the history as the latest update. - UploadHandler(ctx context.Context, getter storage.Getter, putter storage.Putter, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) io.Closer } @@ -43,13 +41,12 @@ var _ Controller = (*controller)(nil) func (c *controller) DownloadHandler( ctx context.Context, - getter storage.Getter, + ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64, ) (swarm.Address, error) { - ls := loadsave.NewReadonly(getter) history, err := NewHistoryReference(ls, historyRootHash) if err != nil { return swarm.ZeroAddress, err @@ -68,13 +65,11 @@ func (c *controller) DownloadHandler( func (c *controller) UploadHandler( ctx context.Context, - getter storage.Getter, - putter storage.Putter, + ls file.LoadSaver, refrefence swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, ) (swarm.Address, swarm.Address, swarm.Address, error) { - ls := loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, false, redundancy.NONE)) historyRef := historyRootHash var ( storage kvs.KeyValueStore @@ -134,8 +129,8 @@ func NewController(accessLogic ActLogic) Controller { func (c *controller) HandleGrantees( ctx context.Context, - getter storage.Getter, - putter storage.Putter, + ls file.LoadSaver, + gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, @@ -147,8 +142,6 @@ func (c *controller) HandleGrantees( h History act kvs.KeyValueStore granteeref swarm.Address - ls = loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, false, redundancy.NONE)) - gls = loadsave.New(getter, putter, requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) ) if !historyref.IsZero() { h, err = NewHistoryReference(ls, historyref) @@ -250,8 +243,7 @@ func (c *controller) HandleGrantees( return glref, eglref, href, actref, nil } -func (c *controller) GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { - ls := loadsave.NewReadonly(getter) +func (c *controller) GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { granteeRef, err := c.decryptRefForPublisher(publisher, encryptedglref) if err != nil { return nil, err diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 5298ac8e7e7..0f6950dcf20 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -10,6 +10,8 @@ import ( "github.com/ethersphere/bee/v2/pkg/dynamicaccess" encryption "github.com/ethersphere/bee/v2/pkg/encryption" "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/kvs" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" @@ -52,9 +54,9 @@ func TestController_NewUpload(t *testing.T) { al := dynamicaccess.NewLogic(diffieHellman) c := dynamicaccess.NewController(al) ref := swarm.RandAddress(t) - _, hRef, encRef, err := c.UploadHandler(ctx, mockStorer.ChunkStore(), mockStorer.Cache(), ref, &publisher.PublicKey, swarm.ZeroAddress) - ls := createLs() + _, hRef, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress) + h, err := dynamicaccess.NewHistoryReference(ls, hRef) entry, err := h.Lookup(ctx, time.Now().Unix()) actRef := entry.Reference() @@ -82,7 +84,7 @@ func TestController_PublisherDownload(t *testing.T) { encRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, mockStorer.ChunkStore(), encRef, &publisher.PublicKey, href, time.Now().Unix()) + dref, err := c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, href, time.Now().Unix()) assert.NoError(t, err) assert.Equal(t, ref, dref) } @@ -108,7 +110,7 @@ func TestController_GranteeDownload(t *testing.T) { encRef, err := publisherAL.EncryptRef(ctx, act, &publisher.PublicKey, ref) assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, mockStorer.ChunkStore(), encRef, &publisher.PublicKey, href, ts) + dref, err := c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, href, ts) assert.NoError(t, err) assert.Equal(t, ref, dref) } @@ -121,8 +123,7 @@ func TestController_HandleGrantees(t *testing.T) { keys, _ := al.Session.Key(&publisher.PublicKey, [][]byte{{1}}) refCipher := encryption.New(keys[0], 0, uint32(0), sha3.NewLegacyKeccak256) ls := createLs() - getter := mockStorer.ChunkStore() - putter := mockStorer.Cache() + gls := loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), true, redundancy.NONE)) c := dynamicaccess.NewController(al) href, _ := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) @@ -131,38 +132,38 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add to new list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) - gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) }) t.Run("add to existing list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} - granteeRef, _, _, _, err = c.HandleGrantees(ctx, getter, putter, eglref, href, &publisher.PublicKey, addList, nil) - gl, err = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + gl, err = dynamicaccess.NewGranteeListReference(ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) }) t.Run("add and revoke", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} revokeList := []*ecdsa.PublicKey{&grantee1.PublicKey} - gl, _ := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(ls) gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) granteeRef, err := gl.Save(ctx) eglref, _ := refCipher.Encrypt(granteeRef.Bytes()) - granteeRef, _, _, _, err = c.HandleGrantees(ctx, getter, putter, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) - gl, err = dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) + gl, err = dynamicaccess.NewGranteeListReference(ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) @@ -170,8 +171,8 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add twice", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} - granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - granteeRef, _, _, _, err = c.HandleGrantees(ctx, getter, putter, eglref, href, &publisher.PublicKey, addList, nil) + granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -179,7 +180,7 @@ func TestController_HandleGrantees(t *testing.T) { }) t.Run("revoke non-existing", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, _, _, err := c.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -197,16 +198,15 @@ func TestController_GetGrantees(t *testing.T) { al1 := dynamicaccess.NewLogic(diffieHellman1) al2 := dynamicaccess.NewLogic(diffieHellman2) ls := createLs() - getter := mockStorer.ChunkStore() - putter := mockStorer.Cache() + gls := loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), true, redundancy.NONE)) c1 := dynamicaccess.NewController(al1) c2 := dynamicaccess.NewController(al2) t.Run("get by publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, eglRef, _, _, err := c1.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, eglRef, _, _, err := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) - grantees, err := c1.GetGrantees(ctx, getter, &publisher.PublicKey, eglRef) + grantees, err := c1.GetGrantees(ctx, ls, &publisher.PublicKey, eglRef) assert.NoError(t, err) assert.True(t, reflect.DeepEqual(grantees, addList)) @@ -215,8 +215,8 @@ func TestController_GetGrantees(t *testing.T) { }) t.Run("get by non-publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - _, eglRef, _, _, err := c1.HandleGrantees(ctx, getter, putter, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) - grantees, err := c2.GetGrantees(ctx, getter, &publisher.PublicKey, eglRef) + _, eglRef, _, _, err := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + grantees, err := c2.GetGrantees(ctx, ls, &publisher.PublicKey, eglRef) assert.Error(t, err) assert.Nil(t, grantees) }) diff --git a/pkg/dynamicaccess/mock/controller.go b/pkg/dynamicaccess/mock/controller.go index 10f4c2823a1..09cacb65145 100644 --- a/pkg/dynamicaccess/mock/controller.go +++ b/pkg/dynamicaccess/mock/controller.go @@ -78,7 +78,7 @@ func WithPublisher(ref string) Option { }) } -func (m *mockDacService) DownloadHandler(ctx context.Context, getter storage.Getter, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { +func (m *mockDacService) DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { if m.acceptAll { return swarm.ParseHexAddress("36e6c1bbdfee6ac21485d5f970479fd1df458d36df9ef4e8179708ed46da557f") } @@ -101,7 +101,7 @@ func (m *mockDacService) DownloadHandler(ctx context.Context, getter storage.Get return m.refMap[encryptedRef.String()], nil } -func (m *mockDacService) UploadHandler(ctx context.Context, getter storage.Getter, putter storage.Putter, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockDacService) UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") kvsRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") if m.acceptAll { @@ -140,7 +140,7 @@ func (m *mockDacService) Close() error { return nil } -func (m *mockDacService) HandleGrantees(ctx context.Context, getter storage.Getter, putter storage.Putter, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockDacService) HandleGrantees(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") eglRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") @@ -148,7 +148,7 @@ func (m *mockDacService) HandleGrantees(ctx context.Context, getter storage.Gett return glRef, eglRef, historyRef, actref, nil } -func (m *mockDacService) GetGrantees(ctx context.Context, getter storage.Getter, publisher *ecdsa.PublicKey, granteeref swarm.Address) ([]*ecdsa.PublicKey, error) { +func (m *mockDacService) GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { if m.publisher == "" { return nil, fmt.Errorf("granteelist not found") } From f7b7560fe469e20edfe9e9889275d7c1c7f9f021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= Date: Tue, 7 May 2024 12:44:57 +0200 Subject: [PATCH 10/14] FIX: re-init history so that it can be saved; only add publisher if histroy is zero --- pkg/api/dynamicaccess_test.go | 2 +- pkg/dynamicaccess/controller.go | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index 4b503f9ffcb..a2d0cd6e4b2 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -845,7 +845,7 @@ func TestDacGrantees(t *testing.T) { }) t.Run("get-grantees-unauthorized", func(t *testing.T) { - jsonhttptest.Request(t, client, http.MethodGet, "/grantee/fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396", http.StatusUnauthorized, + jsonhttptest.Request(t, client, http.MethodGet, "/grantee/fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396", http.StatusNotFound, jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ Message: "granteelist not found", Code: http.StatusNotFound, diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 3764ea2d31f..da725511169 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -199,9 +199,11 @@ func (c *controller) HandleGrantees( var granteesToAdd []*ecdsa.PublicKey // generate new access key and new act if len(removeList) != 0 || encryptedglref.IsZero() { - err = c.accessLogic.AddPublisher(ctx, act, publisher) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + if historyref.IsZero() { + err = c.accessLogic.AddPublisher(ctx, act, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } granteesToAdd = gl.Get() } else { @@ -229,6 +231,13 @@ func (c *controller) HandleGrantees( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } + // need to re-initialize history, because Lookup loads the forks causing the manifest save to skip the root node + if !historyref.IsZero() { + h, err = NewHistoryReference(ls, historyref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + } mtdt := map[string]string{"encryptedglref": eglref.String()} err = h.Add(ctx, actref, nil, &mtdt) From 6fa9c4e31064f3f85f88f0edab2edba6ef9afb22 Mon Sep 17 00:00:00 2001 From: Kexort Date: Tue, 7 May 2024 14:08:22 +0200 Subject: [PATCH 11/14] make act timestamp optional --- pkg/api/dynamicaccess.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 2ba951c5d67..b69a984fa92 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -8,6 +8,7 @@ import ( "errors" "io" "net/http" + "time" "github.com/btcsuite/btcd/btcec/v2" "github.com/ethersphere/bee/v2/pkg/crypto" @@ -89,18 +90,23 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { } // Try to download the file wihtout decryption, if the act headers are not present - if headers.Publisher == nil || headers.Timestamp == nil || headers.HistoryAddress == nil { + if headers.Publisher == nil || headers.HistoryAddress == nil { h.ServeHTTP(w, r) return } + timestamp := time.Now().Unix() + if headers.Timestamp == nil { + timestamp = *headers.Timestamp + } + cache := true if headers.Cache != nil { cache = *headers.Cache } ctx := r.Context() ls := loadsave.NewReadonly(s.storer.Download(cache)) - reference, err := s.dac.DownloadHandler(ctx, ls, paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) + reference, err := s.dac.DownloadHandler(ctx, ls, paths.Address, headers.Publisher, *headers.HistoryAddress, timestamp) if err != nil { jsonhttp.InternalServerError(w, errActDownload) return From 933c5f855b626a51d9a3fa0976de990ff962c86b Mon Sep 17 00:00:00 2001 From: Kexort Date: Tue, 7 May 2024 14:08:45 +0200 Subject: [PATCH 12/14] fix revoke grantees --- pkg/dynamicaccess/controller.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index da725511169..ad5ab1f1c8f 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -162,10 +162,6 @@ func (c *controller) HandleGrantees( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - act, err = kvs.New(ls) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } } var gl GranteeList @@ -185,9 +181,11 @@ func (c *controller) HandleGrantees( return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } - err = gl.Add(addList) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + if len(addList) != 0 { + err = gl.Add(addList) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } if len(removeList) != 0 { err = gl.Remove(removeList) @@ -199,11 +197,13 @@ func (c *controller) HandleGrantees( var granteesToAdd []*ecdsa.PublicKey // generate new access key and new act if len(removeList) != 0 || encryptedglref.IsZero() { - if historyref.IsZero() { - err = c.accessLogic.AddPublisher(ctx, act, publisher) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } + act, err = kvs.New(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + err = c.accessLogic.AddPublisher(ctx, act, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } granteesToAdd = gl.Get() } else { From 7960a49e4d7e95fcf33443ac1d6fba5699a8fc77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= Date: Tue, 7 May 2024 16:43:50 +0200 Subject: [PATCH 13/14] Fix: Act timestamp header nil check; Uploadhandler UT --- pkg/api/dynamicaccess.go | 2 +- pkg/dynamicaccess/controller.go | 8 +++-- pkg/dynamicaccess/controller_test.go | 51 ++++++++++++++++++++++------ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index b69a984fa92..bbf3abbc69e 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -96,7 +96,7 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { } timestamp := time.Now().Unix() - if headers.Timestamp == nil { + if headers.Timestamp != nil { timestamp = *headers.Timestamp } diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index ad5ab1f1c8f..5deb69c9ffe 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -201,9 +201,11 @@ func (c *controller) HandleGrantees( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - err = c.accessLogic.AddPublisher(ctx, act, publisher) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + if historyref.IsZero() { + err = c.accessLogic.AddPublisher(ctx, act, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } granteesToAdd = gl.Get() } else { diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 0f6950dcf20..2b3065899a1 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -47,25 +47,52 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess. return h.Store(ctx) } -func TestController_NewUpload(t *testing.T) { +func TestController_UploadHandler(t *testing.T) { ctx := context.Background() publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) c := dynamicaccess.NewController(al) - ref := swarm.RandAddress(t) ls := createLs() - _, hRef, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress) - h, err := dynamicaccess.NewHistoryReference(ls, hRef) - entry, err := h.Lookup(ctx, time.Now().Unix()) - actRef := entry.Reference() - act, err := kvs.NewReference(ls, actRef) - expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + t.Run("New upload", func(t *testing.T) { + ref := swarm.RandAddress(t) + _, hRef, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress) + assert.NoError(t, err) - assert.NoError(t, err) - assert.Equal(t, encRef, expRef) - assert.NotEqual(t, hRef, swarm.ZeroAddress) + h, _ := dynamicaccess.NewHistoryReference(ls, hRef) + entry, _ := h.Lookup(ctx, time.Now().Unix()) + actRef := entry.Reference() + act, _ := kvs.NewReference(ls, actRef) + expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + + assert.NoError(t, err) + assert.Equal(t, encRef, expRef) + assert.NotEqual(t, hRef, swarm.ZeroAddress) + }) + + t.Run("Upload to same history", func(t *testing.T) { + ref := swarm.RandAddress(t) + _, hRef1, _, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress) + assert.NoError(t, err) + _, hRef2, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, hRef1) + assert.NoError(t, err) + h, err := dynamicaccess.NewHistoryReference(ls, hRef2) + assert.NoError(t, err) + hRef2, err = h.Store(ctx) + assert.NoError(t, err) + assert.True(t, hRef1.Equal(hRef2)) + + h, _ = dynamicaccess.NewHistoryReference(ls, hRef2) + entry, _ := h.Lookup(ctx, time.Now().Unix()) + actRef := entry.Reference() + act, _ := kvs.NewReference(ls, actRef) + expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + + assert.NoError(t, err) + assert.Equal(t, encRef, expRef) + assert.NotEqual(t, hRef2, swarm.ZeroAddress) + }) } func TestController_PublisherDownload(t *testing.T) { @@ -133,6 +160,7 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add to new list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} granteeRef, _, _, _, err := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + assert.NoError(t, err) gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) @@ -142,6 +170,7 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add to existing list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + assert.NoError(t, err) gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) From 81e3b0417bbe202c1f501e5065c4a0300e3116a1 Mon Sep 17 00:00:00 2001 From: Kexort Date: Mon, 13 May 2024 14:54:37 +0200 Subject: [PATCH 14/14] Fix controller nil pointer deref --- pkg/dynamicaccess/controller.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 5deb69c9ffe..a2c9ad50d04 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -162,6 +162,15 @@ func (c *controller) HandleGrantees( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } + // generate new access key and new act + act, err = kvs.New(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + err = c.accessLogic.AddPublisher(ctx, act, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } var gl GranteeList @@ -195,17 +204,15 @@ func (c *controller) HandleGrantees( } var granteesToAdd []*ecdsa.PublicKey - // generate new access key and new act if len(removeList) != 0 || encryptedglref.IsZero() { + // generate new access key and new act act, err = kvs.New(ls) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - if historyref.IsZero() { - err = c.accessLogic.AddPublisher(ctx, act, publisher) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } + err = c.accessLogic.AddPublisher(ctx, act, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } granteesToAdd = gl.Get() } else {