storj/satellite/console/consolewasm/permission_test.go

393 lines
15 KiB
Go
Raw Normal View History

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package consolewasm_test
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/require"
"storj.io/common/testcontext"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite/console/consolewasm"
"storj.io/uplink"
)
func TestSetPermissionWithBuckets(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
satellitePeer := planet.Satellites[0]
satelliteNodeURL := satellitePeer.NodeURL().String()
uplinkPeer := planet.Uplinks[0]
APIKey := uplinkPeer.APIKey[satellitePeer.ID()]
apiKeyString := APIKey.Serialize()
projectID := uplinkPeer.Projects[0].ID.String()
require.Equal(t, 1, len(uplinkPeer.Projects))
passphrase := "supersecretpassphrase"
// Create an access grant with the uplink API key. With that access grant, create 2 buckets and upload an object.
uplinkAccess, err := uplinkPeer.Config.RequestAccessWithPassphrase(ctx, satelliteNodeURL, apiKeyString, passphrase)
require.NoError(t, err)
uplinkPeer.Access[satellitePeer.ID()] = uplinkAccess
testbucket1 := "buckettest1"
testbucket2 := "buckettest2"
testfilename := "file.txt"
testdata := []byte("fun data")
require.NoError(t, uplinkPeer.CreateBucket(ctx, satellitePeer, testbucket1))
require.NoError(t, uplinkPeer.CreateBucket(ctx, satellitePeer, testbucket2))
require.NoError(t, uplinkPeer.Upload(ctx, satellitePeer, testbucket1, testfilename, testdata))
require.NoError(t, uplinkPeer.Upload(ctx, satellitePeer, testbucket2, testfilename, testdata))
data, err := uplinkPeer.Download(ctx, satellitePeer, testbucket1, testfilename)
require.NoError(t, err)
require.Equal(t, data, testdata)
buckets := []string{testbucket1}
// Restrict the uplink access grant with read only permissions and only allows actions for 1 bucket.
var sharePrefixes []uplink.SharePrefix
for _, path := range buckets {
sharePrefixes = append(sharePrefixes, uplink.SharePrefix{
Bucket: path,
})
}
restrictedUplinkAccess, err := uplinkAccess.Share(uplink.ReadOnlyPermission(), sharePrefixes...)
require.NoError(t, err)
// Expect that we can download the object with the restricted access for the 1 allowed bucket.
uplinkPeer.Access[satellitePeer.ID()] = restrictedUplinkAccess
uplinkPeer.APIKey[satellitePeer.ID()] = APIKey
data, err = uplinkPeer.Download(ctx, satellitePeer, testbucket1, testfilename)
require.NoError(t, err)
require.Equal(t, data, testdata)
err = uplinkPeer.Upload(ctx, satellitePeer, testbucket1, "file2", testdata)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = uplinkPeer.Download(ctx, satellitePeer, testbucket2, testfilename)
require.Error(t, err)
// Create restricted access with the console access grant code that allows full access to only 1 bucket.
readOnlyPermission := consolewasm.Permission{
AllowDownload: true,
AllowUpload: false,
AllowList: true,
AllowDelete: false,
NotBefore: time.Now().Add(-24 * time.Hour),
NotAfter: time.Now().Add(48 * time.Hour),
}
restrictedKey, err := consolewasm.SetPermission(apiKeyString, buckets, readOnlyPermission)
require.NoError(t, err)
restrictedAccessGrant, err := consolewasm.GenAccessGrant(satelliteNodeURL, restrictedKey.Serialize(), passphrase, projectID)
require.NoError(t, err)
restrictedAccess, err := uplink.ParseAccess(restrictedAccessGrant)
require.NoError(t, err)
// Expect that we can download the object with the restricted access for the 1 allowed bucket.
uplinkPeer.APIKey[satellitePeer.ID()] = restrictedKey
uplinkPeer.Access[satellitePeer.ID()] = restrictedAccess
data, err = uplinkPeer.Download(ctx, satellitePeer, testbucket1, testfilename)
require.NoError(t, err)
require.Equal(t, data, testdata)
err = uplinkPeer.Upload(ctx, satellitePeer, testbucket1, "file2", testdata)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = uplinkPeer.Download(ctx, satellitePeer, testbucket2, testfilename)
require.Error(t, err)
})
}
func TestSetPermission_Uplink(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
satellitePeer := planet.Satellites[0]
satelliteNodeURL := satellitePeer.NodeURL().String()
uplinkPeer := planet.Uplinks[0]
APIKey := uplinkPeer.APIKey[satellitePeer.ID()]
apiKeyString := APIKey.Serialize()
require.Equal(t, 1, len(uplinkPeer.Projects))
passphrase := "supersecretpassphrase"
// Create an access grant with the uplink API key. With that access grant, create 2 bucket and upload files to them
uplinkAccess, err := uplinkPeer.Config.RequestAccessWithPassphrase(ctx, satelliteNodeURL, apiKeyString, passphrase)
require.NoError(t, err)
uplinkPeer.Access[satellitePeer.ID()] = uplinkAccess
testbucket1 := "buckettest1"
testbucket2 := "buckettest2"
testbucket3 := "buckettest3"
testfilename1 := "file1.txt"
testfilename2 := "file2.txt"
testdata := []byte("fun data")
require.NoError(t, uplinkPeer.CreateBucket(ctx, satellitePeer, testbucket1))
require.NoError(t, uplinkPeer.CreateBucket(ctx, satellitePeer, testbucket2))
require.NoError(t, uplinkPeer.Upload(ctx, satellitePeer, testbucket1, testfilename1, testdata))
require.NoError(t, uplinkPeer.Upload(ctx, satellitePeer, testbucket2, testfilename1, testdata))
require.NoError(t, uplinkPeer.Upload(ctx, satellitePeer, testbucket2, testfilename2, testdata))
withAccessKey(ctx, t, planet, passphrase, "only delete", []string{}, consolewasm.Permission{AllowDelete: true}, func(t *testing.T, project *uplink.Project) {
// All operation except delete should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
upload, err := project.UploadObject(ctx, testbucket2, testfilename2, nil)
require.NoError(t, err)
_, err = upload.Write(testdata)
require.NoError(t, err)
err = upload.Commit()
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DownloadObject(ctx, testbucket2, testfilename1, nil)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
// We can see buckets names, but not the files inside
buckets := getAllBuckets(ctx, project)
require.Equal(t, 2, len(buckets))
objects := getAllObjects(ctx, project, testbucket1)
require.Equal(t, 0, len(objects))
_, err = project.DeleteObject(ctx, testbucket1, testfilename2)
require.NoError(t, err)
// Current implementation needs also permission to Download/Read/List so having
// only Delete permission for DeleteBucketWithObjects won't work
_, err = project.DeleteBucketWithObjects(ctx, testbucket2)
require.Error(t, err)
})
withAccessKey(ctx, t, planet, passphrase, "only list", []string{testbucket1}, consolewasm.Permission{AllowList: true}, func(t *testing.T, project *uplink.Project) {
// All operation except list inside testbucket1 should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
upload, err := project.UploadObject(ctx, testbucket1, testfilename2, nil)
require.NoError(t, err)
_, err = upload.Write(testdata)
require.NoError(t, err)
err = upload.Commit()
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DownloadObject(ctx, testbucket1, testfilename1, nil)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
// Only one bucket should be visible
buckets := getAllBuckets(ctx, project)
require.Equal(t, 1, len(buckets))
objects := getAllObjects(ctx, project, testbucket1)
require.Equal(t, 1, len(objects))
_, err = project.DeleteObject(ctx, testbucket1, testfilename1)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DeleteBucketWithObjects(ctx, testbucket1)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "only upload", []string{testbucket1}, consolewasm.Permission{AllowUpload: true}, func(t *testing.T, project *uplink.Project) {
// All operation except upload to the testbucket1 should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
upload, err := project.UploadObject(ctx, testbucket1, testfilename2, nil)
require.NoError(t, err)
_, err = upload.Write(testdata)
require.NoError(t, err)
err = upload.Commit()
require.NoError(t, err)
_, err = project.DownloadObject(ctx, testbucket1, testfilename1, nil)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
// Only one bucket should be visible
buckets := getAllBuckets(ctx, project)
require.Equal(t, 1, len(buckets))
objects := getAllObjects(ctx, project, testbucket1)
require.Equal(t, 0, len(objects))
_, err = project.DeleteObject(ctx, testbucket1, testfilename1)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DeleteBucketWithObjects(ctx, testbucket1)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "only download", []string{testbucket1}, consolewasm.Permission{AllowDownload: true}, func(t *testing.T, project *uplink.Project) {
// All operation except download from testbucket1 should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
upload, err := project.UploadObject(ctx, testbucket1, testfilename2, nil)
require.NoError(t, err)
_, err = upload.Write(testdata)
require.NoError(t, err)
err = upload.Commit()
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DownloadObject(ctx, testbucket1, testfilename1, nil)
require.NoError(t, err)
// Only one bucket should be visible
buckets := getAllBuckets(ctx, project)
require.Equal(t, 1, len(buckets))
objects := getAllObjects(ctx, project, testbucket1)
require.Equal(t, 0, len(objects))
_, err = project.DeleteObject(ctx, testbucket1, testfilename1)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DeleteBucketWithObjects(ctx, testbucket1)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "not after", []string{}, consolewasm.Permission{
AllowDownload: true,
AllowUpload: true,
AllowList: true,
AllowDelete: true,
NotAfter: time.Now().Add(-2 * time.Hour),
}, func(t *testing.T, project *uplink.Project) {
// All operation should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
upload, err := project.UploadObject(ctx, testbucket1, testfilename2, nil)
require.NoError(t, err)
_, err = upload.Write(testdata)
require.NoError(t, err)
err = upload.Commit()
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DownloadObject(ctx, testbucket1, testfilename1, nil)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DeleteBucketWithObjects(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
buckets := getAllBuckets(ctx, project)
require.Equal(t, 0, len(buckets))
objects := getAllObjects(ctx, project, testbucket1)
require.Equal(t, 0, len(objects))
_, err = project.DeleteObject(ctx, testbucket1, testfilename1)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "not before", []string{}, consolewasm.Permission{
AllowDownload: true,
AllowUpload: true,
AllowList: true,
AllowDelete: true,
NotBefore: time.Now().Add(2 * time.Hour),
}, func(t *testing.T, project *uplink.Project) {
// All operation should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
upload, err := project.UploadObject(ctx, testbucket2, testfilename2, nil)
require.NoError(t, err)
_, err = upload.Write(testdata)
require.NoError(t, err)
err = upload.Commit()
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DownloadObject(ctx, testbucket1, testfilename1, nil)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
_, err = project.DeleteBucketWithObjects(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
buckets := getAllBuckets(ctx, project)
require.Equal(t, 0, len(buckets))
objects := getAllObjects(ctx, project, testbucket1)
require.Equal(t, 0, len(objects))
_, err = project.DeleteObject(ctx, testbucket1, testfilename1)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "all", []string{}, consolewasm.Permission{
AllowDownload: true,
AllowUpload: true,
AllowList: true,
AllowDelete: true,
NotBefore: time.Now().Add(-24 * time.Hour),
NotAfter: time.Now().Add(24 * time.Hour),
}, func(t *testing.T, project *uplink.Project) {
// All operation allowed
_, err = project.CreateBucket(ctx, testbucket3)
require.NoError(t, err)
upload, err := project.UploadObject(ctx, testbucket3, testfilename2, nil)
require.NoError(t, err)
_, err = upload.Write(testdata)
require.NoError(t, err)
err = upload.Commit()
require.NoError(t, err)
objects := getAllObjects(ctx, project, testbucket3)
require.Equal(t, 1, len(objects))
_, err = project.DownloadObject(ctx, testbucket3, testfilename2, nil)
require.NoError(t, err)
_, err = project.DeleteBucketWithObjects(ctx, testbucket3)
require.NoError(t, err)
buckets := getAllBuckets(ctx, project)
require.Equal(t, 2, len(buckets))
_, err = project.DeleteObject(ctx, testbucket1, testfilename1)
require.NoError(t, err)
})
})
}
func getAllObjects(ctx *testcontext.Context, project *uplink.Project, bucket string) []*uplink.Object {
var objects = []*uplink.Object{}
iter := project.ListObjects(ctx, bucket, nil)
for iter.Next() {
objects = append(objects, iter.Item())
}
return objects
}
func getAllBuckets(ctx *testcontext.Context, project *uplink.Project) []*uplink.Bucket {
var buckets = []*uplink.Bucket{}
iter := project.ListBuckets(ctx, nil)
for iter.Next() {
buckets = append(buckets, iter.Item())
}
return buckets
}
func withAccessKey(ctx context.Context, t *testing.T, planet *testplanet.Planet, passphrase string, testname string, bucket []string, permissions consolewasm.Permission, fn func(t *testing.T, uplink *uplink.Project)) {
t.Run(testname, func(t *testing.T) {
upl := planet.Uplinks[0]
sat := planet.Satellites[0]
apikey := upl.APIKey[sat.ID()]
restrictedKey, err := consolewasm.SetPermission(apikey.Serialize(), bucket, permissions)
require.NoError(t, err)
restrictedGrant, err := consolewasm.GenAccessGrant(sat.NodeURL().String(), restrictedKey.Serialize(), passphrase, upl.Projects[0].ID.String())
require.NoError(t, err)
access, err := uplink.ParseAccess(restrictedGrant)
require.NoError(t, err)
project, err := uplink.OpenProject(ctx, access)
require.NoError(t, err)
defer func() { require.NoError(t, project.Close()) }()
fn(t, project)
})
}