From ea105aff2d51c86e3daea48c27f51b6bbf1b6b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:03:37 +0200 Subject: [PATCH] refactor(act): error handling and testing (#64) --- pkg/accesscontrol/access.go | 16 ++- pkg/accesscontrol/access_test.go | 134 ++++++++++++------------ pkg/accesscontrol/controller.go | 6 +- pkg/accesscontrol/controller_test.go | 146 ++++++++++++++++----------- pkg/accesscontrol/grantee_test.go | 99 +++++++++--------- pkg/accesscontrol/history.go | 31 ++++-- pkg/accesscontrol/history_test.go | 51 ++++++---- pkg/accesscontrol/kvs/kvs.go | 9 +- pkg/accesscontrol/kvs/kvs_test.go | 23 +++-- pkg/accesscontrol/kvs/mock/kvs.go | 1 - pkg/accesscontrol/mock/controller.go | 32 ++++-- pkg/accesscontrol/session_test.go | 88 ++++++---------- pkg/api/accesscontrol.go | 59 +++++++---- pkg/api/accesscontrol_test.go | 133 +++++++++++++++++++++--- pkg/api/api.go | 1 + pkg/api/bytes.go | 14 ++- pkg/api/bzz.go | 14 ++- pkg/api/chunk.go | 14 ++- pkg/api/dirs.go | 14 ++- pkg/api/feed.go | 16 ++- pkg/api/soc.go | 14 ++- pkg/manifest/mantaray.go | 22 ++-- 22 files changed, 596 insertions(+), 341 deletions(-) diff --git a/pkg/accesscontrol/access.go b/pkg/accesscontrol/access.go index 79f60facb2d..0b7f9a094ac 100644 --- a/pkg/accesscontrol/access.go +++ b/pkg/accesscontrol/access.go @@ -7,6 +7,7 @@ package accesscontrol import ( "context" "crypto/ecdsa" + "errors" "fmt" "github.com/ethersphere/bee/v2/pkg/accesscontrol/kvs" @@ -51,7 +52,7 @@ func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, pu if err != nil { return swarm.ZeroAddress, err } - refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) + refCipher := encryption.New(accessKey, 0, 0, hashFunc) encryptedRef, err := refCipher.Encrypt(ref.Bytes()) if err != nil { return swarm.ZeroAddress, fmt.Errorf("failed to encrypt reference: %w", err) @@ -84,7 +85,7 @@ func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, pu } // Encrypt the access key for the new Grantee. - cipher := encryption.New(encryption.Key(accessKeyDecryptionKey), 0, uint32(0), hashFunc) + cipher := encryption.New(encryption.Key(accessKeyDecryptionKey), 0, 0, hashFunc) granteeEncryptedAccessKey, err := cipher.Encrypt(accessKey) if err != nil { return fmt.Errorf("failed to encrypt access key: %w", err) @@ -106,10 +107,15 @@ func (al *ActLogic) getAccessKey(ctx context.Context, storage kvs.KeyValueStore, return nil, err } // no need for constructor call if value not found in act. - accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, uint32(0), hashFunc) + accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, 0, hashFunc) encryptedAK, err := storage.Get(ctx, publisherLookupKey) if err != nil { - return nil, fmt.Errorf("failed go get value from KVS: %w", err) + switch { + case errors.Is(err, kvs.ErrNotFound): + return nil, ErrNotFound + default: + return nil, fmt.Errorf("failed go get value from KVS: %w", err) + } } accessKey, err := accessKeyDecryptionCipher.Decrypt(encryptedAK) @@ -137,7 +143,7 @@ func (al ActLogic) DecryptRef(ctx context.Context, storage kvs.KeyValueStore, en return swarm.ZeroAddress, err } - refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) + refCipher := encryption.New(accessKey, 0, 0, hashFunc) ref, err := refCipher.Decrypt(encryptedRef.Bytes()) if err != nil { return swarm.ZeroAddress, fmt.Errorf("failed to decrypt reference: %w", err) diff --git a/pkg/accesscontrol/access_test.go b/pkg/accesscontrol/access_test.go index d976131ec92..0b2eeaa1e56 100644 --- a/pkg/accesscontrol/access_test.go +++ b/pkg/accesscontrol/access_test.go @@ -10,6 +10,7 @@ import ( "crypto/elliptic" "crypto/rand" "encoding/hex" + "fmt" "testing" "github.com/ethersphere/bee/v2/pkg/accesscontrol" @@ -19,6 +20,20 @@ import ( "github.com/stretchr/testify/assert" ) +func assertNoError(t *testing.T, msg string, err error) { + t.Helper() + if err != nil { + assert.FailNowf(t, err.Error(), msg) + } +} + +func assertError(t *testing.T, msg string, err error) { + t.Helper() + if err == nil { + assert.FailNowf(t, fmt.Sprintf("Expected %s error, got nil", msg), "") + } +} + // Generates a new test environment with a fix private key. func setupAccessLogic() accesscontrol.ActLogic { privateKey := getPrivKey(1) @@ -55,91 +70,67 @@ func getPrivKey(keyNumber int) *ecdsa.PrivateKey { return privKey } -func TestDecryptRef_Success(t *testing.T) { +func TestDecryptRef_Publisher(t *testing.T) { t.Parallel() ctx := context.Background() id1 := getPrivKey(1) s := kvsmock.New() al := setupAccessLogic() err := al.AddGrantee(ctx, s, &id1.PublicKey, &id1.PublicKey) - if err != nil { - t.Fatalf("AddGrantee: expected no error, got %v", err) - } + assertNoError(t, "AddGrantee", err) - byteRef, _ := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") + byteRef, err := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") + assertNoError(t, "DecodeString", err) expectedRef := swarm.NewAddress(byteRef) encryptedRef, err := al.EncryptRef(ctx, s, &id1.PublicKey, expectedRef) - if err != nil { - t.Fatalf("There was an error while calling EncryptRef: %v", err) - } - - actualRef, err := al.DecryptRef(ctx, s, encryptedRef, &id1.PublicKey) - if err != nil { - t.Fatalf("There was an error while calling Get: %v", err) - } - - if !expectedRef.Equal(actualRef) { - t.Fatalf("DecryptRef gave back wrong Swarm reference! Expedted: %v, actual: %v", expectedRef, actualRef) - } + assertNoError(t, "al encryptref", err) + + t.Run("decrypt success", func(t *testing.T) { + actualRef, err := al.DecryptRef(ctx, s, encryptedRef, &id1.PublicKey) + assertNoError(t, "decrypt ref", err) + + if !expectedRef.Equal(actualRef) { + assert.FailNowf(t, fmt.Sprintf("DecryptRef gave back wrong Swarm reference! Expedted: %v, actual: %v", expectedRef, actualRef), "") + } + }) + t.Run("decrypt with nil publisher", func(t *testing.T) { + _, err = al.DecryptRef(ctx, s, encryptedRef, nil) + assertError(t, "al decryptref", err) + }) } func TestDecryptRefWithGrantee_Success(t *testing.T) { t.Parallel() ctx := context.Background() - id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + id0, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + assertNoError(t, "GenerateKey", err) diffieHellman := accesscontrol.NewDefaultSession(id0) al := accesscontrol.NewLogic(diffieHellman) s := kvsmock.New() - err := al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) - if err != nil { - t.Fatalf("AddGrantee: expected no error, got %v", err) - } + err = al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) + assertNoError(t, "AddGrantee publisher", err) - id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + id1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + assertNoError(t, "GenerateKey", err) err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey) - if err != nil { - t.Fatalf("AddNewGrantee: expected no error, got %v", err) - } + assertNoError(t, "AddGrantee id1", err) - byteRef, _ := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") + byteRef, err := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") + assertNoError(t, "DecodeString", err) expectedRef := swarm.NewAddress(byteRef) encryptedRef, err := al.EncryptRef(ctx, s, &id0.PublicKey, expectedRef) - if err != nil { - t.Fatalf("There was an error while calling EncryptRef: %v", err) - } + assertNoError(t, "al encryptref", err) diffieHellman2 := accesscontrol.NewDefaultSession(id1) granteeAccessLogic := accesscontrol.NewLogic(diffieHellman2) actualRef, err := granteeAccessLogic.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) - if err != nil { - t.Fatalf("There was an error while calling Get: %v", err) - } + assertNoError(t, "grantee al decryptref", err) if !expectedRef.Equal(actualRef) { - t.Fatalf("DecryptRef gave back wrong Swarm reference! Expedted: %v, actual: %v", expectedRef, actualRef) - } -} - -func TestDecryptRef_Error(t *testing.T) { - t.Parallel() - id0 := getPrivKey(0) - - ctx := context.Background() - s := kvsmock.New() - al := setupAccessLogic() - err := al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) - assert.NoError(t, err) - - expectedRef := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" - - encryptedRef, _ := al.EncryptRef(ctx, s, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) - - r, err := al.DecryptRef(ctx, s, encryptedRef, nil) - if err == nil { - t.Fatalf("Get should return error but got reference: %v", r) + assert.FailNowf(t, fmt.Sprintf("DecryptRef gave back wrong Swarm reference! Expedted: %v, actual: %v", expectedRef, actualRef), "") } } @@ -152,20 +143,20 @@ func TestAddPublisher(t *testing.T) { al := setupAccessLogic() err := al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) - assert.NoError(t, err) + assertNoError(t, "AddGrantee", err) decodedSavedLookupKey, err := hex.DecodeString(savedLookupKey) - assert.NoError(t, err) + assertNoError(t, "decode LookupKey", err) encryptedAccessKey, err := s.Get(ctx, decodedSavedLookupKey) - assert.NoError(t, err) + assertNoError(t, "kvs Get accesskey", err) decodedEncryptedAccessKey := hex.EncodeToString(encryptedAccessKey) // A random value is returned, so it is only possible to check the length of the returned value // We know the lookup key because the generated private key is fixed if len(decodedEncryptedAccessKey) != 64 { - t.Fatalf("AddGrantee: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)) + assert.FailNowf(t, fmt.Sprintf("AddGrantee: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)), "") } } @@ -183,38 +174,41 @@ func TestAddNewGranteeToContent(t *testing.T) { s := kvsmock.New() al := setupAccessLogic() err := al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) - assert.NoError(t, err) + assertNoError(t, "AddGrantee id0", err) err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey) - assert.NoError(t, err) + assertNoError(t, "AddGrantee id1", err) err = al.AddGrantee(ctx, s, &id0.PublicKey, &id2.PublicKey) - assert.NoError(t, err) + assertNoError(t, "AddGrantee id2", err) lookupKeyAsByte, err := hex.DecodeString(publisherLookupKey) - assert.NoError(t, err) + assertNoError(t, "publisher lookupkey DecodeString", err) - result, _ := s.Get(ctx, lookupKeyAsByte) + result, err := s.Get(ctx, lookupKeyAsByte) + assertNoError(t, "1st kvs get", err) hexEncodedEncryptedAK := hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { - t.Fatalf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + assert.FailNowf(t, fmt.Sprintf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)), "") } lookupKeyAsByte, err = hex.DecodeString(firstAddedGranteeLookupKey) - assert.NoError(t, err) + assertNoError(t, "1st lookupkey DecodeString", err) - result, _ = s.Get(ctx, lookupKeyAsByte) + result, err = s.Get(ctx, lookupKeyAsByte) + assertNoError(t, "2nd kvs get", err) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { - t.Fatalf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + assert.FailNowf(t, fmt.Sprintf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)), "") } lookupKeyAsByte, err = hex.DecodeString(secondAddedGranteeLookupKey) - assert.NoError(t, err) + assertNoError(t, "2nd lookupkey DecodeString", err) - result, _ = s.Get(ctx, lookupKeyAsByte) + result, err = s.Get(ctx, lookupKeyAsByte) + assertNoError(t, "3rd kvs get", err) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { - t.Fatalf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + assert.FailNowf(t, fmt.Sprintf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)), "") } } diff --git a/pkg/accesscontrol/controller.go b/pkg/accesscontrol/controller.go index f2c843c6b36..b0a33855e55 100644 --- a/pkg/accesscontrol/controller.go +++ b/pkg/accesscontrol/controller.go @@ -29,7 +29,7 @@ type Grantees interface { Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglRef swarm.Address) ([]*ecdsa.PublicKey, error) } -// Controller the interface for managing access control on Swarm. +// Controller represents an interface for managing access control on Swarm. // It provides methods for handling downloads, uploads and updates for grantee lists and references. type Controller interface { Grantees @@ -265,7 +265,7 @@ func (c *ControllerStruct) encryptRefForPublisher(publisherPubKey *ecdsa.PublicK if err != nil { return swarm.ZeroAddress, err } - refCipher := encryption.New(keys[0], 0, uint32(0), hashFunc) + refCipher := encryption.New(keys[0], 0, 0, hashFunc) encryptedRef, err := refCipher.Encrypt(ref.Bytes()) if err != nil { return swarm.ZeroAddress, fmt.Errorf("failed to encrypt reference: %w", err) @@ -279,7 +279,7 @@ func (c *ControllerStruct) decryptRefForPublisher(publisherPubKey *ecdsa.PublicK if err != nil { return swarm.ZeroAddress, err } - refCipher := encryption.New(keys[0], 0, uint32(0), hashFunc) + refCipher := encryption.New(keys[0], 0, 0, hashFunc) ref, err := refCipher.Decrypt(encryptedRef.Bytes()) if err != nil { return swarm.ZeroAddress, fmt.Errorf("failed to decrypt reference: %w", err) diff --git a/pkg/accesscontrol/controller_test.go b/pkg/accesscontrol/controller_test.go index 11a922cacae..cf1eba59a56 100644 --- a/pkg/accesscontrol/controller_test.go +++ b/pkg/accesscontrol/controller_test.go @@ -24,7 +24,8 @@ import ( ) //nolint:errcheck,gosec,wrapcheck -func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al accesscontrol.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) { +func getHistoryFixture(t *testing.T, ctx context.Context, ls file.LoadSaver, al accesscontrol.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) { + t.Helper() h, err := accesscontrol.NewHistory(ls) if err != nil { return swarm.ZeroAddress, err @@ -32,17 +33,23 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al accesscontrol. pk1 := getPrivKey(1) pk2 := getPrivKey(2) - kvs0, _ := kvs.New(ls) + kvs0, err := kvs.New(ls) + assertNoError(t, "kvs0 create", err) al.AddGrantee(ctx, kvs0, publisher, publisher) - kvs0Ref, _ := kvs0.Save(ctx) - kvs1, _ := kvs.New(ls) + kvs0Ref, err := kvs0.Save(ctx) + assertNoError(t, "kvs0 save", err) + kvs1, err := kvs.New(ls) + assertNoError(t, "kvs1 create", err) al.AddGrantee(ctx, kvs1, publisher, publisher) al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey) - kvs1Ref, _ := kvs1.Save(ctx) - kvs2, _ := kvs.New(ls) + kvs1Ref, err := kvs1.Save(ctx) + assertNoError(t, "kvs1 save", err) + kvs2, err := kvs.New(ls) + assertNoError(t, "kvs2 create", err) al.AddGrantee(ctx, kvs2, publisher, publisher) al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey) - kvs2Ref, _ := kvs2.Save(ctx) + kvs2Ref, err := kvs2.Save(ctx) + assertNoError(t, "kvs2 save", err) 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() thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() @@ -65,15 +72,18 @@ func TestController_UploadHandler(t *testing.T) { 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) + assertNoError(t, "UploadHandler", err) - h, _ := accesscontrol.NewHistoryReference(ls, hRef) - entry, _ := h.Lookup(ctx, time.Now().Unix()) + h, err := accesscontrol.NewHistoryReference(ls, hRef) + assertNoError(t, "create history ref", err) + entry, err := h.Lookup(ctx, time.Now().Unix()) + assertNoError(t, "history lookup", err) actRef := entry.Reference() - act, _ := kvs.NewReference(ls, actRef) + act, err := kvs.NewReference(ls, actRef) + assertNoError(t, "kvs create ref", err) expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) - assert.NoError(t, err) + assertNoError(t, "encrypt ref", err) assert.Equal(t, encRef, expRef) assert.NotEqual(t, hRef, swarm.ZeroAddress) }) @@ -81,22 +91,25 @@ func TestController_UploadHandler(t *testing.T) { 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) + assertNoError(t, "1st upload", err) _, hRef2, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, hRef1) - assert.NoError(t, err) + assertNoError(t, "2nd upload", err) h, err := accesscontrol.NewHistoryReference(ls, hRef2) - assert.NoError(t, err) + assertNoError(t, "create history ref", err) hRef2, err = h.Store(ctx) - assert.NoError(t, err) + assertNoError(t, "store history", err) assert.True(t, hRef1.Equal(hRef2)) - h, _ = accesscontrol.NewHistoryReference(ls, hRef2) - entry, _ := h.Lookup(ctx, time.Now().Unix()) + h, err = accesscontrol.NewHistoryReference(ls, hRef2) + assertNoError(t, "create history ref", err) + entry, err := h.Lookup(ctx, time.Now().Unix()) + assertNoError(t, "history lookup", err) actRef := entry.Reference() - act, _ := kvs.NewReference(ls, actRef) + act, err := kvs.NewReference(ls, actRef) + assertNoError(t, "kvs create ref", err) expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) - assert.NoError(t, err) + assertNoError(t, "encrypt ref", err) assert.Equal(t, expRef, encRef) assert.NotEqual(t, hRef2, swarm.ZeroAddress) }) @@ -111,16 +124,20 @@ func TestController_PublisherDownload(t *testing.T) { c := accesscontrol.NewController(al) ls := createLs() ref := swarm.RandAddress(t) - href, _ := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) - h, _ := accesscontrol.NewHistoryReference(ls, href) - entry, _ := h.Lookup(ctx, time.Now().Unix()) + href, err := getHistoryFixture(t, ctx, ls, al, &publisher.PublicKey) + assertNoError(t, "history fixture create", err) + h, err := accesscontrol.NewHistoryReference(ls, href) + assertNoError(t, "create history ref", err) + entry, err := h.Lookup(ctx, time.Now().Unix()) + assertNoError(t, "history lookup", err) actRef := entry.Reference() - act, _ := kvs.NewReference(ls, actRef) + act, err := kvs.NewReference(ls, actRef) + assertNoError(t, "kvs create ref", err) encRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + assertNoError(t, "encrypt ref", err) - assert.NoError(t, err) dref, err := c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, href, time.Now().Unix()) - assert.NoError(t, err) + assertNoError(t, "download by publisher", err) assert.Equal(t, ref, dref) } @@ -137,17 +154,21 @@ func TestController_GranteeDownload(t *testing.T) { ls := createLs() c := accesscontrol.NewController(al) ref := swarm.RandAddress(t) - href, _ := getHistoryFixture(ctx, ls, publisherAL, &publisher.PublicKey) - h, _ := accesscontrol.NewHistoryReference(ls, href) + href, err := getHistoryFixture(t, ctx, ls, publisherAL, &publisher.PublicKey) + assertNoError(t, "history fixture create", err) + h, err := accesscontrol.NewHistoryReference(ls, href) + assertNoError(t, "history fixture create", err) ts := time.Date(2001, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - entry, _ := h.Lookup(ctx, ts) + entry, err := h.Lookup(ctx, ts) + assertNoError(t, "history lookup", err) actRef := entry.Reference() - act, _ := kvs.NewReference(ls, actRef) + act, err := kvs.NewReference(ls, actRef) + assertNoError(t, "kvs create ref", err) encRef, err := publisherAL.EncryptRef(ctx, act, &publisher.PublicKey, ref) - assert.NoError(t, err) + assertNoError(t, "encrypt ref", err) dref, err := c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, href, ts) - assert.NoError(t, err) + assertNoError(t, "download by grantee", err) assert.Equal(t, ref, dref) } @@ -157,12 +178,14 @@ func TestController_UpdateHandler(t *testing.T) { publisher := getPrivKey(1) diffieHellman := accesscontrol.NewDefaultSession(publisher) al := accesscontrol.NewLogic(diffieHellman) - keys, _ := al.Session.Key(&publisher.PublicKey, [][]byte{{1}}) - refCipher := encryption.New(keys[0], 0, uint32(0), sha3.NewLegacyKeccak256) + keys, err := al.Session.Key(&publisher.PublicKey, [][]byte{{1}}) + assertNoError(t, "Session key", err) + refCipher := encryption.New(keys[0], 0, 0, sha3.NewLegacyKeccak256) ls := createLs() gls := loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), true, redundancy.NONE)) c := accesscontrol.NewController(al) - href, _ := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) + href, err := getHistoryFixture(t, ctx, ls, al, &publisher.PublicKey) + assertNoError(t, "history fixture create", err) grantee1 := getPrivKey(0) grantee := getPrivKey(2) @@ -170,41 +193,45 @@ func TestController_UpdateHandler(t *testing.T) { t.Run("add to new list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} granteeRef, _, _, _, err := c.UpdateHandler(ctx, ls, ls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) - assert.NoError(t, err) + assertNoError(t, "UpdateHandlererror", err) gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) - assert.NoError(t, err) + assertNoError(t, "create granteelist ref", 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.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - assert.NoError(t, err) + assertNoError(t, "UpdateHandlererror", err) gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) - assert.NoError(t, err) + assertNoError(t, "create granteelist ref", err) assert.Len(t, gl.Get(), 1) addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} - granteeRef, _, _, _, _ = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + assertNoError(t, "UpdateHandler", err) gl, err = accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) - assert.NoError(t, err) + assertNoError(t, "create granteelist ref", 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 := accesscontrol.NewGranteeList(ls) - _ = gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) - granteeRef, _ := gl.Save(ctx) - eglref, _ := refCipher.Encrypt(granteeRef.Bytes()) - - granteeRef, _, _, _, _ = c.UpdateHandler(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) - gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) + err = gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) + granteeRef, err := gl.Save(ctx) + assertNoError(t, "granteelist save", err) + eglref, err := refCipher.Encrypt(granteeRef.Bytes()) + assertNoError(t, "encrypt granteeref", err) + + granteeRef, _, _, _, err = c.UpdateHandler(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) + assertNoError(t, "UpdateHandler", err) + gl, err = accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) - assert.NoError(t, err) + assertNoError(t, "create granteelist ref", err) assert.Len(t, gl.Get(), 2) }) t.Run("add and revoke then get from history", func(t *testing.T) { @@ -251,18 +278,20 @@ func TestController_UpdateHandler(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} //nolint:ineffassign,staticcheck,wastedassign granteeRef, eglref, _, _, err := c.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - granteeRef, _, _, _, _ = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + assertNoError(t, "UpdateHandler", err) gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) - assert.NoError(t, err) + assertNoError(t, "create granteelist ref", err) assert.Len(t, gl.Get(), 1) }) t.Run("revoke non-existing", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, _, _, _ := c.UpdateHandler(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err := c.UpdateHandler(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + assertNoError(t, "UpdateHandler", err) gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) - assert.NoError(t, err) + assertNoError(t, "create granteelist ref", err) assert.Len(t, gl.Get(), 1) }) } @@ -284,20 +313,23 @@ func TestController_Get(t *testing.T) { t.Run("get by publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, eglRef, _, _, _ := c1.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, eglRef, _, _, err := c1.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + assertNoError(t, "UpdateHandler", err) grantees, err := c1.Get(ctx, ls, &publisher.PublicKey, eglRef) - assert.NoError(t, err) + assertNoError(t, "get by publisher", err) assert.True(t, reflect.DeepEqual(grantees, addList)) - gl, _ := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) + gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef) + assertNoError(t, "create granteelist ref", err) assert.True(t, reflect.DeepEqual(gl.Get(), addList)) }) t.Run("get by non-publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - _, eglRef, _, _, _ := c1.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + _, eglRef, _, _, err := c1.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + assertNoError(t, "UpdateHandler", err) grantees, err := c2.Get(ctx, ls, &publisher.PublicKey, eglRef) - assert.Error(t, err) + assertError(t, "controller get by non-publisher", err) assert.Nil(t, grantees) }) } diff --git a/pkg/accesscontrol/grantee_test.go b/pkg/accesscontrol/grantee_test.go index 095029fb0ca..f3c54dc907d 100644 --- a/pkg/accesscontrol/grantee_test.go +++ b/pkg/accesscontrol/grantee_test.go @@ -55,22 +55,21 @@ func TestGranteeAddGet(t *testing.T) { t.Parallel() gl := accesscontrol.NewGranteeList(createLs()) keys, err := generateKeyListFixture() - if err != nil { - t.Errorf("key generation error: %v", err) - } + assertNoError(t, "key generation", err) - t.Run("Get empty grantee list should return error", func(t *testing.T) { + t.Run("Get empty grantee list should return", func(t *testing.T) { val := gl.Get() assert.Empty(t, val) }) t.Run("Get should return value equal to put value", func(t *testing.T) { var ( - keys2, _ = generateKeyListFixture() - addList1 = []*ecdsa.PublicKey{keys[0]} - addList2 = []*ecdsa.PublicKey{keys[1], keys[2]} - addList3 = keys2 + keys2, err = generateKeyListFixture() + addList1 = []*ecdsa.PublicKey{keys[0]} + addList2 = []*ecdsa.PublicKey{keys[1], keys[2]} + addList3 = keys2 ) + assertNoError(t, "key generation", err) testCases := []struct { name string list []*ecdsa.PublicKey @@ -102,9 +101,9 @@ func TestGranteeAddGet(t *testing.T) { t.Run(tc.name, func(t *testing.T) { err := gl.Add(tc.list) if tc.list == nil { - assert.Error(t, err) + assertError(t, "granteelist add", err) } else { - assert.NoError(t, err) + assertNoError(t, "granteelist add", err) if tc.name != "Test list = duplicate1" { expList = append(expList, tc.list...) } @@ -120,45 +119,43 @@ func TestGranteeRemove(t *testing.T) { t.Parallel() gl := accesscontrol.NewGranteeList(createLs()) keys, err := generateKeyListFixture() - if err != nil { - t.Errorf("key generation error: %v", err) - } + assertNoError(t, "key generation", err) - t.Run("Add should NOT return error", func(t *testing.T) { + t.Run("Add should NOT return", func(t *testing.T) { err := gl.Add(keys) - assert.NoError(t, err) + assertNoError(t, "granteelist add", err) retVal := gl.Get() assert.Equal(t, keys, retVal) }) removeList1 := []*ecdsa.PublicKey{keys[0]} removeList2 := []*ecdsa.PublicKey{keys[2], keys[1]} - t.Run("Remove the first item should return NO error", func(t *testing.T) { + t.Run("Remove the first item should return NO", func(t *testing.T) { err := gl.Remove(removeList1) - assert.NoError(t, err) + assertNoError(t, "granteelist remove", err) retVal := gl.Get() assert.Equal(t, removeList2, retVal) }) - t.Run("Remove non-existent item should return NO error", func(t *testing.T) { + t.Run("Remove non-existent item should return NO", func(t *testing.T) { err := gl.Remove(removeList1) - assert.NoError(t, err) + assertNoError(t, "granteelist remove", err) retVal := gl.Get() assert.Equal(t, removeList2, retVal) }) - t.Run("Remove second and third item should return NO error", func(t *testing.T) { + t.Run("Remove second and third item should return NO", func(t *testing.T) { err := gl.Remove(removeList2) - assert.NoError(t, err) + assertNoError(t, "granteelist remove", err) retVal := gl.Get() assert.Empty(t, retVal) }) - t.Run("Remove from empty grantee list should return error", func(t *testing.T) { + t.Run("Remove from empty grantee list should return", func(t *testing.T) { err := gl.Remove(removeList1) - assert.Error(t, err) + assertError(t, "remove from empty grantee list", err) retVal := gl.Get() assert.Empty(t, retVal) }) - t.Run("Remove empty remove list should return error", func(t *testing.T) { + t.Run("Remove empty remove list should return", func(t *testing.T) { err := gl.Remove(nil) - assert.Error(t, err) + assertError(t, "remove empty list", err) retVal := gl.Get() assert.Empty(t, retVal) }) @@ -168,24 +165,24 @@ func TestGranteeSave(t *testing.T) { t.Parallel() ctx := context.Background() keys, err := generateKeyListFixture() - if err != nil { - t.Errorf("key generation error: %v", err) - } - t.Run("Create grantee list with invalid reference, expect error", func(t *testing.T) { + assertNoError(t, "key generation", err) + + t.Run("Create grantee list with invalid reference, expect", func(t *testing.T) { gl, err := accesscontrol.NewGranteeListReference(ctx, createLs(), swarm.RandAddress(t)) - assert.Error(t, err) + assertError(t, "create grantee list ref", err) assert.Nil(t, gl) }) - t.Run("Save empty grantee list return NO error", func(t *testing.T) { + t.Run("Save empty grantee list return NO", func(t *testing.T) { gl := accesscontrol.NewGranteeList(createLs()) _, err := gl.Save(ctx) - assert.NoError(t, err) + assertNoError(t, "granteelist save", err) }) t.Run("Save not empty grantee list return valid swarm address", func(t *testing.T) { gl := accesscontrol.NewGranteeList(createLs()) err = gl.Add(keys) + assertNoError(t, "granteelist add", err) ref, err := gl.Save(ctx) - assert.NoError(t, err) + assertNoError(t, "granteelist save", err) assert.True(t, ref.IsValidNonEmpty()) }) t.Run("Save grantee list with one item, no error, pre-save value exist", func(t *testing.T) { @@ -193,30 +190,34 @@ func TestGranteeSave(t *testing.T) { gl1 := accesscontrol.NewGranteeList(ls) err := gl1.Add(keys) - assert.NoError(t, err) + assertNoError(t, "granteelist add", err) ref, err := gl1.Save(ctx) - assert.NoError(t, err) + assertNoError(t, "1st granteelist save", err) - gl2, _ := accesscontrol.NewGranteeListReference(ctx, ls, ref) + gl2, err := accesscontrol.NewGranteeListReference(ctx, ls, ref) + assertNoError(t, "create grantee list ref", err) val := gl2.Get() - assert.NoError(t, err) + assertNoError(t, "2nd granteelist save", 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() + keys2, err := generateKeyListFixture() + assertNoError(t, "key generation", err) gl1 := accesscontrol.NewGranteeList(ls) - err := gl1.Add(keys) - assert.NoError(t, err) + err = gl1.Add(keys) + assertNoError(t, "granteelist1 add", err) + ref, err := gl1.Save(ctx) - assert.NoError(t, err) + assertNoError(t, "granteelist1 save", err) - gl2, _ := accesscontrol.NewGranteeListReference(ctx, ls, ref) + gl2, err := accesscontrol.NewGranteeListReference(ctx, ls, ref) + assertNoError(t, "create grantee list ref", err) err = gl2.Add(keys2) - assert.NoError(t, err) + assertNoError(t, "create grantee list ref", err) val := gl2.Get() assert.Equal(t, append(keys, keys2...), val) @@ -226,11 +227,11 @@ func TestGranteeSave(t *testing.T) { func TestGranteeRemoveTwo(t *testing.T) { gl := accesscontrol.NewGranteeList(createLs()) keys, err := generateKeyListFixture() - if err != nil { - t.Errorf("key generation error: %v", err) - } - _ = gl.Add([]*ecdsa.PublicKey{keys[0]}) - _ = gl.Add([]*ecdsa.PublicKey{keys[0]}) + assertNoError(t, "key generation", err) + err = gl.Add([]*ecdsa.PublicKey{keys[0]}) + assertNoError(t, "1st granteelist add", err) + err = gl.Add([]*ecdsa.PublicKey{keys[0]}) + assertNoError(t, "2nd granteelist add", err) err = gl.Remove([]*ecdsa.PublicKey{keys[0]}) - assert.NoError(t, err) + assertNoError(t, "granteelist remove", err) } diff --git a/pkg/accesscontrol/history.go b/pkg/accesscontrol/history.go index a5a203834f2..b8aba6e5cc9 100644 --- a/pkg/accesscontrol/history.go +++ b/pkg/accesscontrol/history.go @@ -25,6 +25,8 @@ var ( ErrUnexpectedType = errors.New("unexpected type") // ErrInvalidTimestamp indicates that the timestamp given to Lookup is invalid. ErrInvalidTimestamp = errors.New("invalid timestamp") + // ErrNotFound is returned when an Entry is not found in the history. + ErrNotFound = errors.New("access control: not found") ) // History represents the interface for managing access control history. @@ -39,20 +41,26 @@ type History interface { var _ History = (*HistoryStruct)(nil) +// manifestInterface extends the `manifest.Interface` interface and adds a `Root` method. +type manifestInterface interface { + manifest.Interface + Root() *mantaray.Node +} + // HistoryStruct represents an access control histroy with a mantaray-based manifest. type HistoryStruct struct { - manifest *manifest.MantarayManifest + manifest manifestInterface ls file.LoadSaver } // NewHistory creates a new history with a mantaray-based manifest. func NewHistory(ls file.LoadSaver) (*HistoryStruct, error) { - m, err := manifest.NewDefaultManifest(ls, false) + m, err := manifest.NewMantarayManifest(ls, false) if err != nil { - return nil, fmt.Errorf("failed to create default manifest: %w", err) + return nil, fmt.Errorf("failed to create mantaray manifest: %w", err) } - mm, ok := m.(*manifest.MantarayManifest) + mm, ok := m.(manifestInterface) if !ok { return nil, fmt.Errorf("%w: expected MantarayManifest, got %T", ErrUnexpectedType, m) } @@ -62,12 +70,12 @@ func NewHistory(ls file.LoadSaver) (*HistoryStruct, error) { // NewHistoryReference loads a history with a mantaray-based manifest. func NewHistoryReference(ls file.LoadSaver, ref swarm.Address) (*HistoryStruct, error) { - m, err := manifest.NewDefaultManifestReference(ref, ls) + m, err := manifest.NewMantarayManifestReference(ref, ls) if err != nil { - return nil, fmt.Errorf("failed to create default manifest reference: %w", err) + return nil, fmt.Errorf("failed to create mantaray manifest reference: %w", err) } - mm, ok := m.(*manifest.MantarayManifest) + mm, ok := m.(manifestInterface) if !ok { return nil, fmt.Errorf("%w: expected MantarayManifest, got %T", ErrUnexpectedType, m) } @@ -105,14 +113,19 @@ func (h *HistoryStruct) Lookup(ctx context.Context, timestamp int64) (manifest.E reversedTimestamp := math.MaxInt64 - timestamp node, err := h.lookupNode(ctx, reversedTimestamp) if err != nil { - return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), err + switch { + case errors.Is(err, manifest.ErrNotFound): + return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), ErrNotFound + default: + return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), err + } } if node != nil { return manifest.NewEntry(swarm.NewAddress(node.Entry()), node.Metadata()), nil } - return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), nil + return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), ErrNotFound } func (h *HistoryStruct) lookupNode(ctx context.Context, searchedTimestamp int64) (*mantaray.Node, error) { diff --git a/pkg/accesscontrol/history_test.go b/pkg/accesscontrol/history_test.go index a9c1aaa939f..7c4d0ff0168 100644 --- a/pkg/accesscontrol/history_test.go +++ b/pkg/accesscontrol/history_test.go @@ -23,14 +23,14 @@ import ( func TestHistoryAdd(t *testing.T) { t.Parallel() h, err := accesscontrol.NewHistory(nil) - assert.NoError(t, err) + assertNoError(t, "create history", err) addr := swarm.NewAddress([]byte("addr")) ctx := context.Background() err = h.Add(ctx, addr, nil, nil) - assert.NoError(t, err) + assertNoError(t, "history add", err) } func TestSingleNodeHistoryLookup(t *testing.T) { @@ -40,19 +40,19 @@ func TestSingleNodeHistoryLookup(t *testing.T) { ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) h, err := accesscontrol.NewHistory(ls) - assert.NoError(t, err) + assertNoError(t, "create history", err) testActRef := swarm.RandAddress(t) err = h.Add(ctx, testActRef, nil, nil) - assert.NoError(t, err) + assertNoError(t, "history add", err) _, err = h.Store(ctx) - assert.NoError(t, err) + assertNoError(t, "store history", err) searchedTime := time.Now().Unix() entry, err := h.Lookup(ctx, searchedTime) + assertNoError(t, "history lookup", err) actRef := entry.Reference() - assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef)) assert.Nil(t, entry.Metadata()) } @@ -63,62 +63,68 @@ func TestMultiNodeHistoryLookup(t *testing.T) { ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) - h, _ := accesscontrol.NewHistory(ls) + h, err := accesscontrol.NewHistory(ls) + assertNoError(t, "create history", err) testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt1 := map[string]string{"firstTime": "1994-04-01"} - _ = h.Add(ctx, testActRef1, &firstTime, &mtdt1) + err = h.Add(ctx, testActRef1, &firstTime, &mtdt1) + assertNoError(t, "1st history add", err) testActRef2 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd892")) secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt2 := map[string]string{"secondTime": "2000-04-01"} - _ = h.Add(ctx, testActRef2, &secondTime, &mtdt2) + err = h.Add(ctx, testActRef2, &secondTime, &mtdt2) + assertNoError(t, "2nd history add", err) testActRef3 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd893")) thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt3 := map[string]string{"thirdTime": "2015-04-01"} - _ = h.Add(ctx, testActRef3, &thirdTime, &mtdt3) + err = h.Add(ctx, testActRef3, &thirdTime, &mtdt3) + assertNoError(t, "3rd history add", err) testActRef4 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd894")) fourthTime := time.Date(2020, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt4 := map[string]string{"fourthTime": "2020-04-01"} - _ = h.Add(ctx, testActRef4, &fourthTime, &mtdt4) + err = h.Add(ctx, testActRef4, &fourthTime, &mtdt4) + assertNoError(t, "4th history add", err) testActRef5 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd895")) fifthTime := time.Date(2030, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt5 := map[string]string{"fifthTime": "2030-04-01"} - _ = h.Add(ctx, testActRef5, &fifthTime, &mtdt5) + err = h.Add(ctx, testActRef5, &fifthTime, &mtdt5) + assertNoError(t, "5th history add", err) // latest searchedTime := time.Date(1980, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() entry, err := h.Lookup(ctx, searchedTime) + assertNoError(t, "1st history lookup", err) actRef := entry.Reference() - assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef1)) assert.True(t, reflect.DeepEqual(mtdt1, entry.Metadata())) // before first time searchedTime = time.Date(2021, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() entry, err = h.Lookup(ctx, searchedTime) + assertNoError(t, "2nd history lookup", err) actRef = entry.Reference() - assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef4)) assert.True(t, reflect.DeepEqual(mtdt4, entry.Metadata())) // same time searchedTime = time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() entry, err = h.Lookup(ctx, searchedTime) + assertNoError(t, "3rd history lookup", err) actRef = entry.Reference() - assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef2)) assert.True(t, reflect.DeepEqual(mtdt2, entry.Metadata())) // after time searchedTime = time.Date(2045, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() entry, err = h.Lookup(ctx, searchedTime) + assertNoError(t, "4th history lookup", err) actRef = entry.Reference() - assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef5)) assert.True(t, reflect.DeepEqual(mtdt5, entry.Metadata())) } @@ -129,20 +135,23 @@ func TestHistoryStore(t *testing.T) { ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) - h1, _ := accesscontrol.NewHistory(ls) + h1, err := accesscontrol.NewHistory(ls) + assertNoError(t, "create history", err) testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt1 := map[string]string{"firstTime": "1994-04-01"} - _ = h1.Add(ctx, testActRef1, &firstTime, &mtdt1) + err = h1.Add(ctx, testActRef1, &firstTime, &mtdt1) + assertNoError(t, "history add", err) href1, err := h1.Store(ctx) - assert.NoError(t, err) + assertNoError(t, "store history", err) h2, err := accesscontrol.NewHistoryReference(ls, href1) - assert.NoError(t, err) + assertNoError(t, "create history ref", err) - entry1, _ := h2.Lookup(ctx, firstTime) + entry1, err := h2.Lookup(ctx, firstTime) + assertNoError(t, "history lookup", err) actRef1 := entry1.Reference() assert.True(t, actRef1.Equal(testActRef1)) assert.True(t, reflect.DeepEqual(mtdt1, entry1.Metadata())) diff --git a/pkg/accesscontrol/kvs/kvs.go b/pkg/accesscontrol/kvs/kvs.go index 6f5e35742b8..a5f644e60fe 100644 --- a/pkg/accesscontrol/kvs/kvs.go +++ b/pkg/accesscontrol/kvs/kvs.go @@ -22,6 +22,8 @@ import ( var ( // ErrNothingToSave indicates that no new key-value pair was added to the store. ErrNothingToSave = errors.New("nothing to save") + // ErrNotFound is returned when an Entry is not found in the storage. + ErrNotFound = errors.New("kvs entry not found") ) // KeyValueStore represents a key-value store. @@ -45,7 +47,12 @@ var _ KeyValueStore = (*keyValueStore)(nil) func (s *keyValueStore) Get(ctx context.Context, key []byte) ([]byte, error) { entry, err := s.manifest.Lookup(ctx, hex.EncodeToString(key)) if err != nil { - return nil, fmt.Errorf("failed to get value from manifest %w", err) + switch { + case errors.Is(err, manifest.ErrNotFound): + return nil, ErrNotFound + default: + return nil, fmt.Errorf("failed to get value from manifest %w", err) + } } ref := entry.Reference() return ref.Bytes(), nil diff --git a/pkg/accesscontrol/kvs/kvs_test.go b/pkg/accesscontrol/kvs/kvs_test.go index 90abac1adfa..ca2227e8f11 100644 --- a/pkg/accesscontrol/kvs/kvs_test.go +++ b/pkg/accesscontrol/kvs/kvs_test.go @@ -128,22 +128,26 @@ func TestKvs_Save(t *testing.T) { key1, val1 := keyValuePair(t) key2, val2 := keyValuePair(t) t.Run("Save empty KVS return error", func(t *testing.T) { - s, _ := kvs.New(createLs()) - _, err := s.Save(ctx) - assert.Error(t, err) + s, err := kvs.New(createLs()) + assert.NoError(t, err) + _, err = s.Save(ctx) + assert.ErrorIs(t, err, kvs.ErrNothingToSave) }) t.Run("Save not empty KVS return valid swarm address", func(t *testing.T) { - s, _ := kvs.New(createLs()) - _ = s.Put(ctx, key1, val1) + s, err := kvs.New(createLs()) + assert.NoError(t, err) + err = s.Put(ctx, key1, val1) + assert.NoError(t, err) ref, err := s.Save(ctx) assert.NoError(t, err) assert.True(t, ref.IsValidNonEmpty()) }) t.Run("Save KVS with one item, no error, pre-save value exist", func(t *testing.T) { ls := createLs() - s1, _ := kvs.New(ls) + s1, err := kvs.New(ls) + assert.NoError(t, err) - err := s1.Put(ctx, key1, val1) + err = s1.Put(ctx, key1, val1) assert.NoError(t, err) ref, err := s1.Save(ctx) @@ -159,9 +163,10 @@ func TestKvs_Save(t *testing.T) { t.Run("Save KVS and add one item, no error, after-save value exist", func(t *testing.T) { ls := createLs() - kvs1, _ := kvs.New(ls) + kvs1, err := kvs.New(ls) + assert.NoError(t, err) - err := kvs1.Put(ctx, key1, val1) + err = kvs1.Put(ctx, key1, val1) assert.NoError(t, err) ref, err := kvs1.Save(ctx) assert.NoError(t, err) diff --git a/pkg/accesscontrol/kvs/mock/kvs.go b/pkg/accesscontrol/kvs/mock/kvs.go index bdd0d54cc12..0d5b1eb8569 100644 --- a/pkg/accesscontrol/kvs/mock/kvs.go +++ b/pkg/accesscontrol/kvs/mock/kvs.go @@ -17,7 +17,6 @@ import ( var lock = &sync.Mutex{} type single struct { - // TODO string -> []byte ? memoryMock map[string]map[string][]byte } diff --git a/pkg/accesscontrol/mock/controller.go b/pkg/accesscontrol/mock/controller.go index 15e87e6ce0c..efbe53c6013 100644 --- a/pkg/accesscontrol/mock/controller.go +++ b/pkg/accesscontrol/mock/controller.go @@ -54,7 +54,7 @@ func New(o ...Option) accesscontrol.Controller { historyMap: make(map[string]accesscontrol.History), refMap: make(map[string]swarm.Address), publisher: "", - encrypter: encryption.New(encryption.Key("b6ee086390c280eeb9824c331a4427596f0c8510d5564bc1b6168d0059a46e2b"), 0, uint32(0), sha3.NewLegacyKeccak256), + encrypter: encryption.New(encryption.Key("b6ee086390c280eeb9824c331a4427596f0c8510d5564bc1b6168d0059a46e2b"), 0, 0, sha3.NewLegacyKeccak256), ls: loadsave.New(storer.ChunkStore(), storer.Cache(), requestPipelineFactory(context.Background(), storer.Cache(), false, redundancy.NONE)), } for _, v := range o { @@ -80,7 +80,7 @@ func WithHistory(h accesscontrol.History, ref string) Option { func WithPublisher(ref string) Option { return optionFunc(func(m *mockController) { m.publisher = ref - m.encrypter = encryption.New(encryption.Key(ref), 0, uint32(0), sha3.NewLegacyKeccak256) + m.encrypter = encryption.New(encryption.Key(ref), 0, 0, sha3.NewLegacyKeccak256) }) } @@ -92,17 +92,20 @@ func (m *mockController) DownloadHandler(ctx context.Context, ls file.LoadSaver, publicKeyBytes := crypto.EncodeSecp256k1PublicKey(publisher) p := hex.EncodeToString(publicKeyBytes) if m.publisher != "" && m.publisher != p { - return swarm.ZeroAddress, fmt.Errorf("incorrect publisher") + return swarm.ZeroAddress, accesscontrol.ErrInvalidPublicKey } h, exists := m.historyMap[historyRootHash.String()] if !exists { - return swarm.ZeroAddress, fmt.Errorf("history not found") + return swarm.ZeroAddress, accesscontrol.ErrNotFound } entry, err := h.Lookup(ctx, timestamp) + if err != nil { + return swarm.ZeroAddress, err + } kvsRef := entry.Reference() - if kvsRef.IsZero() || err != nil { - return swarm.ZeroAddress, fmt.Errorf("kvs not found") + if kvsRef.IsZero() { + return swarm.ZeroAddress, err } return m.refMap[encryptedRef.String()], nil } @@ -118,14 +121,24 @@ func (m *mockController) UploadHandler(ctx context.Context, ls file.LoadSaver, r h accesscontrol.History exists bool ) + + publicKeyBytes := crypto.EncodeSecp256k1PublicKey(publisher) + p := hex.EncodeToString(publicKeyBytes) + if m.publisher != "" && m.publisher != p { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, accesscontrol.ErrInvalidPublicKey + } + now := time.Now().Unix() if !historyRootHash.IsZero() { historyRef = historyRootHash h, exists = m.historyMap[historyRef.String()] if !exists { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, fmt.Errorf("history not found") + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, accesscontrol.ErrNotFound + } + entry, err := h.Lookup(ctx, now) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - entry, _ := h.Lookup(ctx, now) kvsRef := entry.Reference() if kvsRef.IsZero() { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, fmt.Errorf("kvs not found") @@ -147,6 +160,9 @@ func (m *mockController) Close() error { } func (m *mockController) UpdateHandler(_ 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) { + if historyref.Equal(swarm.EmptyAddress) || encryptedglref.Equal(swarm.EmptyAddress) { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, accesscontrol.ErrNotFound + } historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") eglRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") diff --git a/pkg/accesscontrol/session_test.go b/pkg/accesscontrol/session_test.go index 60b175b01a3..a8dc4628f83 100644 --- a/pkg/accesscontrol/session_test.go +++ b/pkg/accesscontrol/session_test.go @@ -9,6 +9,7 @@ import ( "crypto/ecdsa" "crypto/rand" "encoding/hex" + "fmt" "io" "testing" @@ -16,6 +17,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/accesscontrol/mock" "github.com/ethersphere/bee/v2/pkg/crypto" memkeystore "github.com/ethersphere/bee/v2/pkg/keystore/mem" + "github.com/stretchr/testify/assert" ) func mockKeyFunc(*ecdsa.PublicKey, [][]byte) ([][]byte, error) { @@ -24,12 +26,10 @@ func mockKeyFunc(*ecdsa.PublicKey, [][]byte) ([][]byte, error) { func TestSessionNewDefaultSession(t *testing.T) { pk, err := crypto.GenerateSecp256k1Key() - if err != nil { - t.Fatalf("Error generating private key: %v", err) - } + assertNoError(t, "generating private key", err) si := accesscontrol.NewDefaultSession(pk) if si == nil { - t.Fatal("Session instance is nil") + assert.FailNow(t, "Session instance is nil") } } @@ -37,7 +37,7 @@ func TestSessionNewFromKeystore(t *testing.T) { ks := memkeystore.New() si := mock.NewFromKeystore(ks, "tag", "password", mockKeyFunc) if si == nil { - t.Fatal("Session instance is nil") + assert.FailNow(t, "Session instance is nil") } } @@ -45,38 +45,30 @@ func TestSessionKey(t *testing.T) { t.Parallel() key1, err := crypto.GenerateSecp256k1Key() - if err != nil { - t.Fatal(err) - } + assertNoError(t, "key1 GenerateSecp256k1Key", err) si1 := accesscontrol.NewDefaultSession(key1) key2, err := crypto.GenerateSecp256k1Key() - if err != nil { - t.Fatal(err) - } + assertNoError(t, "key2 GenerateSecp256k1Key", err) si2 := accesscontrol.NewDefaultSession(key2) nonces := make([][]byte, 2) for i := range nonces { if _, err := io.ReadFull(rand.Reader, nonces[i]); err != nil { - t.Fatal(err) + assert.FailNow(t, err.Error()) } } keys1, err := si1.Key(&key2.PublicKey, nonces) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "", err) keys2, err := si2.Key(&key1.PublicKey, nonces) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "", err) if !bytes.Equal(keys1[0], keys2[0]) { - t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + assert.FailNowf(t, fmt.Sprintf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])), "") } if !bytes.Equal(keys1[1], keys2[1]) { - t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + assert.FailNowf(t, fmt.Sprintf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[1]), hex.EncodeToString(keys2[1])), "") } } @@ -84,28 +76,20 @@ func TestSessionKeyWithoutNonces(t *testing.T) { t.Parallel() key1, err := crypto.GenerateSecp256k1Key() - if err != nil { - t.Fatal(err) - } + assertNoError(t, "key1 GenerateSecp256k1Key", err) si1 := accesscontrol.NewDefaultSession(key1) key2, err := crypto.GenerateSecp256k1Key() - if err != nil { - t.Fatal(err) - } + assertNoError(t, "key2 GenerateSecp256k1Key", err) si2 := accesscontrol.NewDefaultSession(key2) keys1, err := si1.Key(&key2.PublicKey, nil) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "session1 key", err) keys2, err := si2.Key(&key1.PublicKey, nil) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "session2 key", err) if !bytes.Equal(keys1[0], keys2[0]) { - t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + assert.FailNowf(t, fmt.Sprintf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])), "") } } @@ -120,53 +104,43 @@ func TestSessionKeyFromKeystore(t *testing.T) { si1 := mock.NewFromKeystore(ks, tag1, password1, mockKeyFunc) exists, err := ks.Exists(tag1) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "", err) if !exists { - t.Fatal("Key1 should exist") + assert.FailNow(t, "Key1 should exist") + } key1, created, err := ks.Key(tag1, password1, crypto.EDGSecp256_K1) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "", err) if created { - t.Fatal("Key1 should not be created") + assert.FailNow(t, "Key1 should not be created") + } si2 := mock.NewFromKeystore(ks, tag2, password2, mockKeyFunc) exists, err = ks.Exists(tag2) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "", err) if !exists { - t.Fatal("Key2 should exist") + assert.FailNow(t, "Key2 should exist") } key2, created, err := ks.Key(tag2, password2, crypto.EDGSecp256_K1) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "", err) if created { - t.Fatal("Key2 should not be created") + assert.FailNow(t, "Key2 should not be created") } nonces := make([][]byte, 1) for i := range nonces { if _, err := io.ReadFull(rand.Reader, nonces[i]); err != nil { - t.Fatal(err) + assert.FailNow(t, err.Error()) } } keys1, err := si1.Key(&key2.PublicKey, nonces) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "", err) keys2, err := si2.Key(&key1.PublicKey, nonces) - if err != nil { - t.Fatal(err) - } + assertNoError(t, "", err) if !bytes.Equal(keys1[0], keys2[0]) { - t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + assert.FailNowf(t, fmt.Sprintf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])), "") } } diff --git a/pkg/api/accesscontrol.go b/pkg/api/accesscontrol.go index 11c0453014c..8767eb5a823 100644 --- a/pkg/api/accesscontrol.go +++ b/pkg/api/accesscontrol.go @@ -16,6 +16,7 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/file/loadsave" "github.com/ethersphere/bee/v2/pkg/file/redundancy" @@ -128,9 +129,20 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { ls := loadsave.NewReadonly(s.storer.Download(cache)) reference, err := s.accesscontrol.DownloadHandler(ctx, ls, paths.Address, headers.Publisher, *headers.HistoryAddress, timestamp) if err != nil { - logger.Debug("act failed to decrypt reference", "error", err) - logger.Error(nil, "act failed to decrypt reference") - jsonhttp.InternalServerError(w, errActDownload) + logger.Debug("access control download failed", "error", err) + logger.Error(nil, "access control download failed") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrInvalidTimestamp): + jsonhttp.BadRequest(w, "invalid timestamp") + case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity): + jsonhttp.BadRequest(w, "invalid public key") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActDownload) + } return } h.ServeHTTP(w, r.WithContext(setAddressInContext(ctx, reference))) @@ -147,28 +159,21 @@ func (s *Service) actEncryptionHandler( reference swarm.Address, historyRootHash swarm.Address, ) (swarm.Address, error) { - logger := s.logger.WithName("act_encryption_handler").Build() publisherPublicKey := &s.publicKey ls := loadsave.New(s.storer.Download(true), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) storageReference, historyReference, encryptedReference, err := s.accesscontrol.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") - return swarm.ZeroAddress, fmt.Errorf("act failed to encrypt reference: %w", err) + return swarm.ZeroAddress, err } // only need to upload history and kvs if a new history is created, // meaning that the publisher uploaded to the history for the first time if !historyReference.Equal(historyRootHash) { err = putter.Done(storageReference) if err != nil { - logger.Debug("done split keyvaluestore failed", "error", err) - logger.Error(nil, "done split keyvaluestore failed") - return swarm.ZeroAddress, fmt.Errorf("done split keyvaluestore failed: %w", err) + return swarm.ZeroAddress, fmt.Errorf("done split key-value store failed: %w", err) } err = putter.Done(historyReference) if err != nil { - logger.Debug("done split history failed", "error", err) - logger.Error(nil, "done split history failed") return swarm.ZeroAddress, fmt.Errorf("done split history failed: %w", err) } } @@ -300,7 +305,7 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) 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") + jsonhttp.BadRequest(w, "invalid add list") return } grantees.Addlist = append(grantees.Addlist, parsedAddlist...) @@ -309,7 +314,7 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) 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") + jsonhttp.BadRequest(w, "invalid revoke list") return } grantees.Revokelist = append(grantees.Revokelist, parsedRevokelist...) @@ -347,7 +352,16 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) 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") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrNoGranteeFound): + jsonhttp.BadRequest(w, "remove from empty grantee list") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActGranteeList) + } return } @@ -456,7 +470,7 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques if err != nil { 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") + jsonhttp.BadRequest(w, "invalid grantee list") return } @@ -490,9 +504,16 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques gls := loadsave.New(s.storer.Download(true), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) granteeref, encryptedglref, historyref, actref, err := s.accesscontrol.UpdateHandler(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") - jsonhttp.InternalServerError(w, "failed to update grantee list") + logger.Debug("failed to create grantee list", "error", err) + logger.Error(nil, "failed to create grantee list") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActGranteeList) + } return } diff --git a/pkg/api/accesscontrol_test.go b/pkg/api/accesscontrol_test.go index 11b3e02f28e..e8662a48118 100644 --- a/pkg/api/accesscontrol_test.go +++ b/pkg/api/accesscontrol_test.go @@ -63,8 +63,6 @@ func prepareHistoryFixture(storer api.Storer) (accesscontrol.History, swarm.Addr return h, ref } -// TODO: test tag, pin, deferred, stamp -// TODO: feed test // nolint:paralleltest,tparallel // TestAccessLogicEachEndpointWithAct [positive tests]: // On each endpoint: upload w/ "Swarm-Act" header then download and check the decrypted data @@ -310,13 +308,13 @@ func TestAccessLogicWithoutAct(t *testing.T) { jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", rootHash)), ) - jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(rootHash), http.StatusInternalServerError, + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(rootHash), http.StatusNotFound, jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: api.ErrActDownload.Error(), - Code: http.StatusInternalServerError, + Message: "act or history entry not found", + Code: http.StatusNotFound, }), jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), ) @@ -497,13 +495,13 @@ func TestAccessLogicHistory(t *testing.T) { jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", encryptedRef)), ) - jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusInternalServerError, + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusNotFound, jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, "fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396"), jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: api.ErrActDownload.Error(), - Code: http.StatusInternalServerError, + Message: "act or history entry not found", + Code: http.StatusNotFound, }), jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), ) @@ -519,14 +517,14 @@ func TestAccessLogicHistory(t *testing.T) { }) testfile := "testfile1" - jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusInternalServerError, + jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusNotFound, jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), jsonhttptest.WithRequestBody(strings.NewReader(testfile)), jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: api.ErrActUpload.Error(), - Code: http.StatusInternalServerError, + Message: "act or history entry not found", + Code: http.StatusNotFound, }), jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), ) @@ -631,6 +629,30 @@ func TestAccessLogicTimestamp(t *testing.T) { jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), ) }) + t.Run("download-w/-invalid-timestamp", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + AccessControl: mockac.New(mockac.WithHistory(h, fixtureHref.String())), + }) + var ( + invalidTime = int64(-1) + encryptedRef = "c611199e1b3674d6bf89a83e518bd16896bf5315109b4a23dcb4682a02d17b97" + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(invalidTime, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: accesscontrol.ErrInvalidTimestamp.Error(), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + ) + }) } // nolint:paralleltest,tparallel @@ -768,18 +790,44 @@ func TestAccessLogicPublisher(t *testing.T) { AccessControl: mockac.New(mockac.WithHistory(h, fixtureHref.String()), mockac.WithPublisher(publisher)), }) - jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusInternalServerError, + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusBadRequest, jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, downloader), jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: api.ErrActDownload.Error(), - Code: http.StatusInternalServerError, + Message: accesscontrol.ErrInvalidPublicKey.Error(), + Code: http.StatusBadRequest, }), jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), ) }) + t.Run("re-upload-with-invalid-publickey", func(t *testing.T) { + var ( + downloader = "03c712a7e29bc792ac8d8ae49793d28d5bda27ed70f0d90697b2fb456c0a168bd2" + testfile = "testfile1" + ) + downloaderClient, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + AccessControl: mockac.New(mockac.WithPublisher(downloader)), + }) + + jsonhttptest.Request(t, downloaderClient, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid public key", + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + ) + + }) + t.Run("download-w/o-publisher", func(t *testing.T) { encryptedRef := "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" client, _, _, _ := newTestServer(t, testServerOptions{ @@ -871,6 +919,49 @@ func TestAccessLogicGrantees(t *testing.T) { jsonhttptest.WithJSONRequestBody(body), ) }) + t.Run("add-revoke-grantees-wrong-history", func(t *testing.T) { + body := api.GranteesPatchRequest{ + Addlist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + Revokelist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + } + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusNotFound, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, swarm.EmptyAddress.String()), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "act or history entry not found", + Code: http.StatusNotFound, + }), + jsonhttptest.WithJSONRequestBody(body), + ) + }) + t.Run("invlaid-add-grantees", func(t *testing.T) { + body := api.GranteesPatchRequest{ + Addlist: []string{"random-string"}, + } + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, addr.String()), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "invalid add list", + Code: http.StatusBadRequest, + }), + jsonhttptest.WithJSONRequestBody(body), + ) + }) + t.Run("invlaid-revoke-grantees", func(t *testing.T) { + body := api.GranteesPatchRequest{ + Revokelist: []string{"random-string"}, + } + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, addr.String()), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "invalid revoke list", + Code: http.StatusBadRequest, + }), + 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), @@ -933,4 +1024,18 @@ func TestAccessLogicGrantees(t *testing.T) { }), ) }) + t.Run("create-granteelist-invalid-body", func(t *testing.T) { + body := api.GranteesPostRequest{ + GranteeList: []string{"random-string"}, + } + jsonhttptest.Request(t, client, http.MethodPost, "/grantee", http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(bytes.NewReader(nil)), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "invalid grantee list", + Code: http.StatusBadRequest, + }), + jsonhttptest.WithJSONRequestBody(body), + ) + }) } diff --git a/pkg/api/api.go b/pkg/api/api.go index 38f0aac866f..3f72859a041 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -122,6 +122,7 @@ var ( errOperationSupportedOnlyInFullMode = errors.New("operation is supported only in full mode") errActDownload = errors.New("act download failed") errActUpload = errors.New("act upload failed") + errActGranteeList = errors.New("failed to create or update grantee list") ) // Storer interface provides the functionality required from the local storage diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index 0eefb581c1b..7c9c4dceafa 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -11,6 +11,7 @@ import ( "net/http" "strconv" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" "github.com/ethersphere/bee/v2/pkg/cac" "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/jsonhttp" @@ -120,7 +121,18 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { if headers.Act { encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress) if err != nil { - jsonhttp.InternalServerError(w, errActUpload) + logger.Debug("access control upload failed", "error", err) + logger.Error(nil, "access control upload failed") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity): + jsonhttp.BadRequest(w, "invalid public key") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActUpload) + } return } } diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index bed074ff3ed..ddfa0768303 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -21,6 +21,7 @@ import ( olog "github.com/opentracing/opentracing-go/log" "github.com/ethereum/go-ethereum/common" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" "github.com/ethersphere/bee/v2/pkg/feeds" "github.com/ethersphere/bee/v2/pkg/file/joiner" "github.com/ethersphere/bee/v2/pkg/file/loadsave" @@ -268,7 +269,18 @@ func (s *Service) fileUploadHandler( if act { reference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, historyAddress) if err != nil { - jsonhttp.InternalServerError(w, errActUpload) + logger.Debug("access control upload failed", "error", err) + logger.Error(nil, "access control upload failed") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity): + jsonhttp.BadRequest(w, "invalid public key") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActUpload) + } return } } diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index 534e2924866..1d1416636ed 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -12,6 +12,7 @@ import ( "net/http" "strconv" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" "github.com/ethersphere/bee/v2/pkg/cac" "github.com/ethersphere/bee/v2/pkg/soc" @@ -145,7 +146,18 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { if headers.Act { reference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress) if err != nil { - jsonhttp.InternalServerError(w, errActUpload) + logger.Debug("access control upload failed", "error", err) + logger.Error(nil, "access control upload failed") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity): + jsonhttp.BadRequest(w, "invalid public key") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActUpload) + } return } } diff --git a/pkg/api/dirs.go b/pkg/api/dirs.go index f187fbde01e..23be6929acc 100644 --- a/pkg/api/dirs.go +++ b/pkg/api/dirs.go @@ -18,6 +18,7 @@ import ( "strconv" "strings" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" "github.com/ethersphere/bee/v2/pkg/file/loadsave" "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/jsonhttp" @@ -104,7 +105,18 @@ func (s *Service) dirUploadHandler( if act { encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, historyAddress) if err != nil { - jsonhttp.InternalServerError(w, errActUpload) + logger.Debug("access control upload failed", "error", err) + logger.Error(nil, "access control upload failed") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity): + jsonhttp.BadRequest(w, "invalid public key") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActUpload) + } return } } diff --git a/pkg/api/feed.go b/pkg/api/feed.go index 552cd03ffa4..7750fd7605c 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -13,6 +13,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" "github.com/ethersphere/bee/v2/pkg/feeds" "github.com/ethersphere/bee/v2/pkg/file/loadsave" "github.com/ethersphere/bee/v2/pkg/jsonhttp" @@ -246,12 +247,23 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { } return } - // 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) if err != nil { - jsonhttp.InternalServerError(w, errActUpload) + logger.Debug("access control upload failed", "error", err) + logger.Error(nil, "access control upload failed") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity): + jsonhttp.BadRequest(w, "invalid public key") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActUpload) + } return } } diff --git a/pkg/api/soc.go b/pkg/api/soc.go index 3ceb462d028..dea71963aa7 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -9,6 +9,7 @@ import ( "io" "net/http" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" "github.com/ethersphere/bee/v2/pkg/cac" "github.com/ethersphere/bee/v2/pkg/jsonhttp" "github.com/ethersphere/bee/v2/pkg/postage" @@ -161,7 +162,18 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { if headers.Act { reference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress) if err != nil { - jsonhttp.InternalServerError(w, errActUpload) + logger.Debug("access control upload failed", "error", err) + logger.Error(nil, "access control upload failed") + switch { + case errors.Is(err, accesscontrol.ErrNotFound): + jsonhttp.NotFound(w, "act or history entry not found") + case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity): + jsonhttp.BadRequest(w, "invalid public key") + case errors.Is(err, accesscontrol.ErrUnexpectedType): + jsonhttp.BadRequest(w, "failed to create history") + default: + jsonhttp.InternalServerError(w, errActUpload) + } return } } diff --git a/pkg/manifest/mantaray.go b/pkg/manifest/mantaray.go index 009e3eab055..4666a80c910 100644 --- a/pkg/manifest/mantaray.go +++ b/pkg/manifest/mantaray.go @@ -20,7 +20,7 @@ const ( ManifestMantarayContentType = "application/bzz-manifest-mantaray+octet-stream" ) -type MantarayManifest struct { +type mantarayManifest struct { trie *mantaray.Node ls file.LoadSaver @@ -31,7 +31,7 @@ func NewMantarayManifest( ls file.LoadSaver, encrypted bool, ) (Interface, error) { - mm := &MantarayManifest{ + mm := &mantarayManifest{ trie: mantaray.New(), ls: ls, } @@ -48,28 +48,28 @@ func NewMantarayManifestReference( reference swarm.Address, ls file.LoadSaver, ) (Interface, error) { - return &MantarayManifest{ + return &mantarayManifest{ trie: mantaray.NewNodeRef(reference.Bytes()), ls: ls, }, nil } -func (m *MantarayManifest) Root() *mantaray.Node { +func (m *mantarayManifest) Root() *mantaray.Node { return m.trie } -func (m *MantarayManifest) Type() string { +func (m *mantarayManifest) Type() string { return ManifestMantarayContentType } -func (m *MantarayManifest) Add(ctx context.Context, path string, entry Entry) error { +func (m *mantarayManifest) Add(ctx context.Context, path string, entry Entry) error { p := []byte(path) e := entry.Reference().Bytes() return m.trie.Add(ctx, p, e, entry.Metadata(), m.ls) } -func (m *MantarayManifest) Remove(ctx context.Context, path string) error { +func (m *mantarayManifest) Remove(ctx context.Context, path string) error { p := []byte(path) err := m.trie.Remove(ctx, p, m.ls) @@ -83,7 +83,7 @@ func (m *MantarayManifest) Remove(ctx context.Context, path string) error { return nil } -func (m *MantarayManifest) Lookup(ctx context.Context, path string) (Entry, error) { +func (m *mantarayManifest) Lookup(ctx context.Context, path string) (Entry, error) { p := []byte(path) node, err := m.trie.LookupNode(ctx, p, m.ls) @@ -104,13 +104,13 @@ func (m *MantarayManifest) Lookup(ctx context.Context, path string) (Entry, erro return entry, nil } -func (m *MantarayManifest) HasPrefix(ctx context.Context, prefix string) (bool, error) { +func (m *mantarayManifest) HasPrefix(ctx context.Context, prefix string) (bool, error) { p := []byte(prefix) return m.trie.HasPrefix(ctx, p, m.ls) } -func (m *MantarayManifest) Store(ctx context.Context, storeSizeFn ...StoreSizeFunc) (swarm.Address, error) { +func (m *mantarayManifest) Store(ctx context.Context, storeSizeFn ...StoreSizeFunc) (swarm.Address, error) { var ls mantaray.LoadSaver if len(storeSizeFn) > 0 { ls = &mantarayLoadSaver{ @@ -131,7 +131,7 @@ func (m *MantarayManifest) Store(ctx context.Context, storeSizeFn ...StoreSizeFu return address, nil } -func (m *MantarayManifest) IterateAddresses(ctx context.Context, fn swarm.AddressIterFunc) error { +func (m *mantarayManifest) IterateAddresses(ctx context.Context, fn swarm.AddressIterFunc) error { reference := swarm.NewAddress(m.trie.Reference()) if swarm.ZeroAddress.Equal(reference) {