github.com/ethersphere/bee/v2@v2.2.0/pkg/accesscontrol/controller_test.go (about)

     1  // Copyright 2024 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package accesscontrol_test
     6  
     7  import (
     8  	"context"
     9  	"crypto/ecdsa"
    10  	"reflect"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/ethersphere/bee/v2/pkg/accesscontrol"
    15  	"github.com/ethersphere/bee/v2/pkg/accesscontrol/kvs"
    16  	encryption "github.com/ethersphere/bee/v2/pkg/encryption"
    17  	"github.com/ethersphere/bee/v2/pkg/file"
    18  	"github.com/ethersphere/bee/v2/pkg/file/loadsave"
    19  	"github.com/ethersphere/bee/v2/pkg/file/redundancy"
    20  	"github.com/ethersphere/bee/v2/pkg/swarm"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  	"golang.org/x/crypto/sha3"
    24  )
    25  
    26  //nolint:errcheck,gosec,wrapcheck
    27  func getHistoryFixture(t *testing.T, ctx context.Context, ls file.LoadSaver, al accesscontrol.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) {
    28  	t.Helper()
    29  	h, err := accesscontrol.NewHistory(ls)
    30  	if err != nil {
    31  		return swarm.ZeroAddress, err
    32  	}
    33  	pk1 := getPrivKey(1)
    34  	pk2 := getPrivKey(2)
    35  
    36  	kvs0, err := kvs.New(ls)
    37  	assertNoError(t, "kvs0 create", err)
    38  	al.AddGrantee(ctx, kvs0, publisher, publisher)
    39  	kvs0Ref, err := kvs0.Save(ctx)
    40  	assertNoError(t, "kvs0 save", err)
    41  	kvs1, err := kvs.New(ls)
    42  	assertNoError(t, "kvs1 create", err)
    43  	al.AddGrantee(ctx, kvs1, publisher, publisher)
    44  	al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey)
    45  	kvs1Ref, err := kvs1.Save(ctx)
    46  	assertNoError(t, "kvs1 save", err)
    47  	kvs2, err := kvs.New(ls)
    48  	assertNoError(t, "kvs2 create", err)
    49  	al.AddGrantee(ctx, kvs2, publisher, publisher)
    50  	al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey)
    51  	kvs2Ref, err := kvs2.Save(ctx)
    52  	assertNoError(t, "kvs2 save", err)
    53  	firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix()
    54  	secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix()
    55  	thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix()
    56  
    57  	h.Add(ctx, kvs0Ref, &thirdTime, nil)
    58  	h.Add(ctx, kvs1Ref, &firstTime, nil)
    59  	h.Add(ctx, kvs2Ref, &secondTime, nil)
    60  	return h.Store(ctx)
    61  }
    62  
    63  func TestController_UploadHandler(t *testing.T) {
    64  	t.Parallel()
    65  	ctx := context.Background()
    66  	publisher := getPrivKey(0)
    67  	diffieHellman := accesscontrol.NewDefaultSession(publisher)
    68  	al := accesscontrol.NewLogic(diffieHellman)
    69  	c := accesscontrol.NewController(al)
    70  	ls := createLs()
    71  
    72  	t.Run("New upload", func(t *testing.T) {
    73  		ref := swarm.RandAddress(t)
    74  		_, hRef, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress)
    75  		assertNoError(t, "UploadHandler", err)
    76  
    77  		h, err := accesscontrol.NewHistoryReference(ls, hRef)
    78  		assertNoError(t, "create history ref", err)
    79  		entry, err := h.Lookup(ctx, time.Now().Unix())
    80  		assertNoError(t, "history lookup", err)
    81  		actRef := entry.Reference()
    82  		act, err := kvs.NewReference(ls, actRef)
    83  		assertNoError(t, "kvs create ref", err)
    84  		expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref)
    85  
    86  		assertNoError(t, "encrypt ref", err)
    87  		assert.Equal(t, encRef, expRef)
    88  		assert.NotEqual(t, hRef, swarm.ZeroAddress)
    89  	})
    90  
    91  	t.Run("Upload to same history", func(t *testing.T) {
    92  		ref := swarm.RandAddress(t)
    93  		_, hRef1, _, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress)
    94  		assertNoError(t, "1st upload", err)
    95  		_, hRef2, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, hRef1)
    96  		assertNoError(t, "2nd upload", err)
    97  		h, err := accesscontrol.NewHistoryReference(ls, hRef2)
    98  		assertNoError(t, "create history ref", err)
    99  		hRef2, err = h.Store(ctx)
   100  		assertNoError(t, "store history", err)
   101  		assert.True(t, hRef1.Equal(hRef2))
   102  
   103  		h, err = accesscontrol.NewHistoryReference(ls, hRef2)
   104  		assertNoError(t, "create history ref", err)
   105  		entry, err := h.Lookup(ctx, time.Now().Unix())
   106  		assertNoError(t, "history lookup", err)
   107  		actRef := entry.Reference()
   108  		act, err := kvs.NewReference(ls, actRef)
   109  		assertNoError(t, "kvs create ref", err)
   110  		expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref)
   111  
   112  		assertNoError(t, "encrypt ref", err)
   113  		assert.Equal(t, expRef, encRef)
   114  		assert.NotEqual(t, hRef2, swarm.ZeroAddress)
   115  	})
   116  }
   117  
   118  func TestController_PublisherDownload(t *testing.T) {
   119  	t.Parallel()
   120  	ctx := context.Background()
   121  	publisher := getPrivKey(0)
   122  	diffieHellman := accesscontrol.NewDefaultSession(publisher)
   123  	al := accesscontrol.NewLogic(diffieHellman)
   124  	c := accesscontrol.NewController(al)
   125  	ls := createLs()
   126  	ref := swarm.RandAddress(t)
   127  	href, err := getHistoryFixture(t, ctx, ls, al, &publisher.PublicKey)
   128  	assertNoError(t, "history fixture create", err)
   129  	h, err := accesscontrol.NewHistoryReference(ls, href)
   130  	assertNoError(t, "create history ref", err)
   131  	entry, err := h.Lookup(ctx, time.Now().Unix())
   132  	assertNoError(t, "history lookup", err)
   133  	actRef := entry.Reference()
   134  	act, err := kvs.NewReference(ls, actRef)
   135  	assertNoError(t, "kvs create ref", err)
   136  	encRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref)
   137  	assertNoError(t, "encrypt ref", err)
   138  
   139  	dref, err := c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, href, time.Now().Unix())
   140  	assertNoError(t, "download by publisher", err)
   141  	assert.Equal(t, ref, dref)
   142  }
   143  
   144  func TestController_GranteeDownload(t *testing.T) {
   145  	t.Parallel()
   146  	ctx := context.Background()
   147  	publisher := getPrivKey(0)
   148  	grantee := getPrivKey(2)
   149  	publisherDH := accesscontrol.NewDefaultSession(publisher)
   150  	publisherAL := accesscontrol.NewLogic(publisherDH)
   151  
   152  	diffieHellman := accesscontrol.NewDefaultSession(grantee)
   153  	al := accesscontrol.NewLogic(diffieHellman)
   154  	ls := createLs()
   155  	c := accesscontrol.NewController(al)
   156  	ref := swarm.RandAddress(t)
   157  	href, err := getHistoryFixture(t, ctx, ls, publisherAL, &publisher.PublicKey)
   158  	assertNoError(t, "history fixture create", err)
   159  	h, err := accesscontrol.NewHistoryReference(ls, href)
   160  	assertNoError(t, "history fixture create", err)
   161  	ts := time.Date(2001, time.April, 1, 0, 0, 0, 0, time.UTC).Unix()
   162  	entry, err := h.Lookup(ctx, ts)
   163  	assertNoError(t, "history lookup", err)
   164  	actRef := entry.Reference()
   165  	act, err := kvs.NewReference(ls, actRef)
   166  	assertNoError(t, "kvs create ref", err)
   167  	encRef, err := publisherAL.EncryptRef(ctx, act, &publisher.PublicKey, ref)
   168  
   169  	assertNoError(t, "encrypt ref", err)
   170  	dref, err := c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, href, ts)
   171  	assertNoError(t, "download by grantee", err)
   172  	assert.Equal(t, ref, dref)
   173  }
   174  
   175  func TestController_UpdateHandler(t *testing.T) {
   176  	t.Parallel()
   177  	ctx := context.Background()
   178  	publisher := getPrivKey(1)
   179  	diffieHellman := accesscontrol.NewDefaultSession(publisher)
   180  	al := accesscontrol.NewLogic(diffieHellman)
   181  	keys, err := al.Session.Key(&publisher.PublicKey, [][]byte{{1}})
   182  	assertNoError(t, "Session key", err)
   183  	refCipher := encryption.New(keys[0], 0, 0, sha3.NewLegacyKeccak256)
   184  	ls := createLs()
   185  	gls := loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), true, redundancy.NONE))
   186  	c := accesscontrol.NewController(al)
   187  	href, err := getHistoryFixture(t, ctx, ls, al, &publisher.PublicKey)
   188  	assertNoError(t, "history fixture create", err)
   189  
   190  	grantee1 := getPrivKey(0)
   191  	grantee := getPrivKey(2)
   192  
   193  	t.Run("add to new list", func(t *testing.T) {
   194  		addList := []*ecdsa.PublicKey{&grantee.PublicKey}
   195  		granteeRef, _, _, _, err := c.UpdateHandler(ctx, ls, ls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil)
   196  		assertNoError(t, "UpdateHandlererror", err)
   197  
   198  		gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef)
   199  
   200  		assertNoError(t, "create granteelist ref", err)
   201  		assert.Len(t, gl.Get(), 1)
   202  	})
   203  	t.Run("add to existing list", func(t *testing.T) {
   204  		addList := []*ecdsa.PublicKey{&grantee.PublicKey}
   205  		granteeRef, eglref, _, _, err := c.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil)
   206  		assertNoError(t, "UpdateHandlererror", err)
   207  
   208  		gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef)
   209  
   210  		assertNoError(t, "create granteelist ref", err)
   211  		assert.Len(t, gl.Get(), 1)
   212  
   213  		addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey}
   214  		granteeRef, _, _, _, err = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil)
   215  		assertNoError(t, "UpdateHandler", err)
   216  		gl, err = accesscontrol.NewGranteeListReference(ctx, ls, granteeRef)
   217  		assertNoError(t, "create granteelist ref", err)
   218  		assert.Len(t, gl.Get(), 2)
   219  	})
   220  	t.Run("add and revoke", func(t *testing.T) {
   221  		addList := []*ecdsa.PublicKey{&grantee.PublicKey}
   222  		revokeList := []*ecdsa.PublicKey{&grantee1.PublicKey}
   223  		gl := accesscontrol.NewGranteeList(ls)
   224  		err = gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey})
   225  		granteeRef, err := gl.Save(ctx)
   226  		assertNoError(t, "granteelist save", err)
   227  		eglref, err := refCipher.Encrypt(granteeRef.Bytes())
   228  		assertNoError(t, "encrypt granteeref", err)
   229  
   230  		granteeRef, _, _, _, err = c.UpdateHandler(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList)
   231  		assertNoError(t, "UpdateHandler", err)
   232  		gl, err = accesscontrol.NewGranteeListReference(ctx, ls, granteeRef)
   233  
   234  		assertNoError(t, "create granteelist ref", err)
   235  		assert.Len(t, gl.Get(), 2)
   236  	})
   237  	t.Run("add and revoke then get from history", func(t *testing.T) {
   238  		addRevokeList := []*ecdsa.PublicKey{&grantee.PublicKey}
   239  		ref := swarm.RandAddress(t)
   240  		_, hRef, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress)
   241  		require.NoError(t, err)
   242  
   243  		// Need to wait a second before each update call so that a new history mantaray fork is created for the new key(timestamp) entry
   244  		time.Sleep(1 * time.Second)
   245  		beforeRevokeTS := time.Now().Unix()
   246  		_, egranteeRef, hrefUpdate1, _, err := c.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, hRef, &publisher.PublicKey, addRevokeList, nil)
   247  		require.NoError(t, err)
   248  
   249  		time.Sleep(1 * time.Second)
   250  		granteeRef, _, hrefUpdate2, _, err := c.UpdateHandler(ctx, ls, gls, egranteeRef, hrefUpdate1, &publisher.PublicKey, nil, addRevokeList)
   251  		require.NoError(t, err)
   252  
   253  		gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef)
   254  		require.NoError(t, err)
   255  		assert.Empty(t, gl.Get())
   256  		// expect history reference to be different after grantee list update
   257  		assert.NotEqual(t, hrefUpdate1, hrefUpdate2)
   258  
   259  		granteeDH := accesscontrol.NewDefaultSession(grantee)
   260  		granteeAl := accesscontrol.NewLogic(granteeDH)
   261  		granteeCtrl := accesscontrol.NewController(granteeAl)
   262  		// download with grantee shall still work with the timestamp before the revoke
   263  		decRef, err := granteeCtrl.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, hrefUpdate2, beforeRevokeTS)
   264  		require.NoError(t, err)
   265  		assert.Equal(t, ref, decRef)
   266  
   267  		// download with grantee shall NOT work with the latest timestamp
   268  		decRef, err = granteeCtrl.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, hrefUpdate2, time.Now().Unix())
   269  		require.Error(t, err)
   270  		assert.Equal(t, swarm.ZeroAddress, decRef)
   271  
   272  		// publisher shall still be able to download with the timestamp before the revoke
   273  		decRef, err = c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, hrefUpdate2, beforeRevokeTS)
   274  		require.NoError(t, err)
   275  		assert.Equal(t, ref, decRef)
   276  	})
   277  	t.Run("add twice", func(t *testing.T) {
   278  		addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey}
   279  		//nolint:ineffassign,staticcheck,wastedassign
   280  		granteeRef, eglref, _, _, err := c.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil)
   281  		granteeRef, _, _, _, err = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil)
   282  		assertNoError(t, "UpdateHandler", err)
   283  		gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef)
   284  
   285  		assertNoError(t, "create granteelist ref", err)
   286  		assert.Len(t, gl.Get(), 1)
   287  	})
   288  	t.Run("revoke non-existing", func(t *testing.T) {
   289  		addList := []*ecdsa.PublicKey{&grantee.PublicKey}
   290  		granteeRef, _, _, _, err := c.UpdateHandler(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil)
   291  		assertNoError(t, "UpdateHandler", err)
   292  		gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef)
   293  
   294  		assertNoError(t, "create granteelist ref", err)
   295  		assert.Len(t, gl.Get(), 1)
   296  	})
   297  }
   298  
   299  func TestController_Get(t *testing.T) {
   300  	t.Parallel()
   301  	ctx := context.Background()
   302  	publisher := getPrivKey(1)
   303  	caller := getPrivKey(0)
   304  	grantee := getPrivKey(2)
   305  	diffieHellman1 := accesscontrol.NewDefaultSession(publisher)
   306  	diffieHellman2 := accesscontrol.NewDefaultSession(caller)
   307  	al1 := accesscontrol.NewLogic(diffieHellman1)
   308  	al2 := accesscontrol.NewLogic(diffieHellman2)
   309  	ls := createLs()
   310  	gls := loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), true, redundancy.NONE))
   311  	c1 := accesscontrol.NewController(al1)
   312  	c2 := accesscontrol.NewController(al2)
   313  
   314  	t.Run("get by publisher", func(t *testing.T) {
   315  		addList := []*ecdsa.PublicKey{&grantee.PublicKey}
   316  		granteeRef, eglRef, _, _, err := c1.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil)
   317  		assertNoError(t, "UpdateHandler", err)
   318  
   319  		grantees, err := c1.Get(ctx, ls, &publisher.PublicKey, eglRef)
   320  		assertNoError(t, "get by publisher", err)
   321  		assert.True(t, reflect.DeepEqual(grantees, addList))
   322  
   323  		gl, err := accesscontrol.NewGranteeListReference(ctx, ls, granteeRef)
   324  		assertNoError(t, "create granteelist ref", err)
   325  		assert.True(t, reflect.DeepEqual(gl.Get(), addList))
   326  	})
   327  	t.Run("get by non-publisher", func(t *testing.T) {
   328  		addList := []*ecdsa.PublicKey{&grantee.PublicKey}
   329  		_, eglRef, _, _, err := c1.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil)
   330  		assertNoError(t, "UpdateHandler", err)
   331  		grantees, err := c2.Get(ctx, ls, &publisher.PublicKey, eglRef)
   332  		assertError(t, "controller get by non-publisher", err)
   333  		assert.Nil(t, grantees)
   334  	})
   335  }