forked from TrueCloudLab/frostfs-rest-gw
[#16] Update SDK to v1.0.0-rc.5
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
0df815ed27
commit
aeb68fdd7a
12 changed files with 313 additions and 559 deletions
|
@ -25,16 +25,16 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/handlers"
|
"github.com/nspcc-dev/neofs-rest-gw/handlers"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
||||||
walletconnect "github.com/nspcc-dev/neofs-rest-gw/internal/wallet-connect"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/container"
|
"github.com/nspcc-dev/neofs-sdk-go/container"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
|
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object/address"
|
|
||||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/policy"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/testcontainers/testcontainers-go"
|
"github.com/testcontainers/testcontainers-go"
|
||||||
|
@ -59,7 +59,7 @@ const (
|
||||||
XBearerOwnerID = "X-Bearer-Owner-Id"
|
XBearerOwnerID = "X-Bearer-Owner-Id"
|
||||||
|
|
||||||
// tests configuration.
|
// tests configuration.
|
||||||
useWalletConnect = false
|
useWalletConnect = true
|
||||||
useLocalEnvironment = false
|
useLocalEnvironment = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -82,11 +82,7 @@ func runLocalTests(ctx context.Context, t *testing.T, key *keys.PrivateKey) {
|
||||||
func runTestInContainer(rootCtx context.Context, t *testing.T, key *keys.PrivateKey) {
|
func runTestInContainer(rootCtx context.Context, t *testing.T, key *keys.PrivateKey) {
|
||||||
aioImage := "nspccdev/neofs-aio-testcontainer:"
|
aioImage := "nspccdev/neofs-aio-testcontainer:"
|
||||||
versions := []string{
|
versions := []string{
|
||||||
//"0.24.0",
|
"0.29.0",
|
||||||
//"0.25.1",
|
|
||||||
//"0.26.1",
|
|
||||||
//"0.27.5",
|
|
||||||
"0.27.7",
|
|
||||||
//"latest",
|
//"latest",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,25 +108,28 @@ func runTests(ctx context.Context, t *testing.T, key *keys.PrivateKey, version s
|
||||||
cancel := runServer(ctx, t, node)
|
cancel := runServer(ctx, t, node)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
var owner user.ID
|
||||||
|
user.IDFromKey(&owner, key.PrivateKey.PublicKey)
|
||||||
|
|
||||||
clientPool := getPool(ctx, t, key, node)
|
clientPool := getPool(ctx, t, key, node)
|
||||||
cnrID := createContainer(ctx, t, clientPool, containerName)
|
cnrID := createContainer(ctx, t, clientPool, owner, containerName)
|
||||||
restrictByEACL(ctx, t, clientPool, cnrID)
|
restrictByEACL(ctx, t, clientPool, cnrID)
|
||||||
|
|
||||||
t.Run("rest auth several tokens "+version, func(t *testing.T) { authTokens(ctx, t) })
|
t.Run("rest auth several tokens "+version, func(t *testing.T) { authTokens(ctx, t) })
|
||||||
t.Run("rest check mix tokens up "+version, func(t *testing.T) { mixTokens(ctx, t, cnrID) })
|
t.Run("rest check mix tokens up "+version, func(t *testing.T) { mixTokens(ctx, t, cnrID) })
|
||||||
|
|
||||||
t.Run("rest put object "+version, func(t *testing.T) { restObjectPut(ctx, t, clientPool, cnrID) })
|
t.Run("rest put object "+version, func(t *testing.T) { restObjectPut(ctx, t, clientPool, cnrID) })
|
||||||
t.Run("rest get object "+version, func(t *testing.T) { restObjectGet(ctx, t, clientPool, cnrID) })
|
t.Run("rest get object "+version, func(t *testing.T) { restObjectGet(ctx, t, clientPool, &owner, cnrID) })
|
||||||
t.Run("rest delete object "+version, func(t *testing.T) { restObjectDelete(ctx, t, clientPool, cnrID) })
|
t.Run("rest delete object "+version, func(t *testing.T) { restObjectDelete(ctx, t, clientPool, &owner, cnrID) })
|
||||||
t.Run("rest search objects "+version, func(t *testing.T) { restObjectsSearch(ctx, t, clientPool, cnrID) })
|
t.Run("rest search objects "+version, func(t *testing.T) { restObjectsSearch(ctx, t, clientPool, &owner, cnrID) })
|
||||||
|
|
||||||
t.Run("rest put container invalid "+version, func(t *testing.T) { restContainerPutInvalid(ctx, t) })
|
t.Run("rest put container invalid "+version, func(t *testing.T) { restContainerPutInvalid(ctx, t) })
|
||||||
t.Run("rest put container "+version, func(t *testing.T) { restContainerPut(ctx, t, clientPool) })
|
t.Run("rest put container "+version, func(t *testing.T) { restContainerPut(ctx, t, clientPool) })
|
||||||
t.Run("rest get container "+version, func(t *testing.T) { restContainerGet(ctx, t, clientPool, cnrID) })
|
t.Run("rest get container "+version, func(t *testing.T) { restContainerGet(ctx, t, owner, cnrID) })
|
||||||
t.Run("rest delete container "+version, func(t *testing.T) { restContainerDelete(ctx, t, clientPool) })
|
t.Run("rest delete container "+version, func(t *testing.T) { restContainerDelete(ctx, t, clientPool, owner) })
|
||||||
t.Run("rest put container eacl "+version, func(t *testing.T) { restContainerEACLPut(ctx, t, clientPool) })
|
t.Run("rest put container eacl "+version, func(t *testing.T) { restContainerEACLPut(ctx, t, clientPool, owner) })
|
||||||
t.Run("rest get container eacl "+version, func(t *testing.T) { restContainerEACLGet(ctx, t, clientPool, cnrID) })
|
t.Run("rest get container eacl "+version, func(t *testing.T) { restContainerEACLGet(ctx, t, clientPool, cnrID) })
|
||||||
t.Run("rest list containers "+version, func(t *testing.T) { restContainerList(ctx, t, clientPool, cnrID) })
|
t.Run("rest list containers "+version, func(t *testing.T) { restContainerList(ctx, t, clientPool, owner, cnrID) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDockerContainer(ctx context.Context, t *testing.T, image string) testcontainers.Container {
|
func createDockerContainer(ctx context.Context, t *testing.T, image string) testcontainers.Container {
|
||||||
|
@ -270,7 +269,7 @@ func authTokens(ctx context.Context, t *testing.T) {
|
||||||
makeAuthTokenRequest(ctx, t, bearers, httpClient)
|
makeAuthTokenRequest(ctx, t, bearers, httpClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mixTokens(ctx context.Context, t *testing.T, cnrID *cid.ID) {
|
func mixTokens(ctx context.Context, t *testing.T, cnrID cid.ID) {
|
||||||
bearers := []*models.Bearer{
|
bearers := []*models.Bearer{
|
||||||
{
|
{
|
||||||
Name: "all-object",
|
Name: "all-object",
|
||||||
|
@ -332,8 +331,8 @@ func checkPutContainerWithError(t *testing.T, httpClient *http.Client, token *ha
|
||||||
checkGWErrorResponse(t, httpClient, request)
|
checkGWErrorResponse(t, httpClient, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDeleteContainerWithError(t *testing.T, httpClient *http.Client, cnrID *cid.ID, token *handlers.BearerToken) {
|
func checkDeleteContainerWithError(t *testing.T, httpClient *http.Client, cnrID cid.ID, token *handlers.BearerToken) {
|
||||||
reqURL, err := url.Parse(testHost + "/v1/containers/" + cnrID.String())
|
reqURL, err := url.Parse(testHost + "/v1/containers/" + cnrID.EncodeToString())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
request, err := http.NewRequest(http.MethodDelete, reqURL.String(), nil)
|
request, err := http.NewRequest(http.MethodDelete, reqURL.String(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -342,20 +341,20 @@ func checkDeleteContainerWithError(t *testing.T, httpClient *http.Client, cnrID
|
||||||
checkGWErrorResponse(t, httpClient, request)
|
checkGWErrorResponse(t, httpClient, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSetEACLContainerWithError(t *testing.T, httpClient *http.Client, cnrID *cid.ID, token *handlers.BearerToken) {
|
func checkSetEACLContainerWithError(t *testing.T, httpClient *http.Client, cnrID cid.ID, token *handlers.BearerToken) {
|
||||||
req := models.Eacl{Records: []*models.Record{}}
|
req := models.Eacl{Records: []*models.Record{}}
|
||||||
body, err := json.Marshal(&req)
|
body, err := json.Marshal(&req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
request, err := http.NewRequest(http.MethodPut, testHost+"/v1/containers/"+cnrID.String()+"/eacl", bytes.NewReader(body))
|
request, err := http.NewRequest(http.MethodPut, testHost+"/v1/containers/"+cnrID.EncodeToString()+"/eacl", bytes.NewReader(body))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, token)
|
prepareCommonHeaders(request.Header, token)
|
||||||
|
|
||||||
checkGWErrorResponse(t, httpClient, request)
|
checkGWErrorResponse(t, httpClient, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPutObjectWithError(t *testing.T, httpClient *http.Client, cnrID *cid.ID, token *handlers.BearerToken) {
|
func checkPutObjectWithError(t *testing.T, httpClient *http.Client, cnrID cid.ID, token *handlers.BearerToken) {
|
||||||
req := &models.ObjectUpload{
|
req := &models.ObjectUpload{
|
||||||
ContainerID: util.NewString(cnrID.String()),
|
ContainerID: util.NewString(cnrID.EncodeToString()),
|
||||||
FileName: util.NewString("newFile.txt"),
|
FileName: util.NewString("newFile.txt"),
|
||||||
Payload: base64.StdEncoding.EncodeToString([]byte("content")),
|
Payload: base64.StdEncoding.EncodeToString([]byte("content")),
|
||||||
}
|
}
|
||||||
|
@ -377,7 +376,7 @@ func checkGWErrorResponse(t *testing.T, httpClient *http.Client, request *http.R
|
||||||
require.Equal(t, models.ErrorTypeGW, *resp.Type)
|
require.Equal(t, models.ErrorTypeGW, *resp.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID *cid.ID) {
|
func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID cid.ID) {
|
||||||
bearer := &models.Bearer{
|
bearer := &models.Bearer{
|
||||||
Object: []*models.Record{{
|
Object: []*models.Record{{
|
||||||
Operation: models.NewOperation(models.OperationPUT),
|
Operation: models.NewOperation(models.OperationPUT),
|
||||||
|
@ -404,7 +403,7 @@ func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnr
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &models.ObjectUpload{
|
req := &models.ObjectUpload{
|
||||||
ContainerID: util.NewString(cnrID.String()),
|
ContainerID: util.NewString(cnrID.EncodeToString()),
|
||||||
FileName: util.NewString("newFile.txt"),
|
FileName: util.NewString("newFile.txt"),
|
||||||
Payload: base64.StdEncoding.EncodeToString([]byte(content)),
|
Payload: base64.StdEncoding.EncodeToString([]byte(content)),
|
||||||
Attributes: []*models.Attribute{{
|
Attributes: []*models.Attribute{{
|
||||||
|
@ -427,17 +426,17 @@ func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnr
|
||||||
doRequest(t, httpClient, request, http.StatusOK, addr)
|
doRequest(t, httpClient, request, http.StatusOK, addr)
|
||||||
|
|
||||||
var CID cid.ID
|
var CID cid.ID
|
||||||
err = CID.Parse(*addr.ContainerID)
|
err = CID.DecodeString(*addr.ContainerID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
var id oid.ID
|
var id oid.ID
|
||||||
err = id.Parse(*addr.ObjectID)
|
err = id.DecodeString(*addr.ObjectID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
objectAddress := address.NewAddress()
|
var objectAddress oid.Address
|
||||||
objectAddress.SetContainerID(&CID)
|
objectAddress.SetContainer(CID)
|
||||||
objectAddress.SetObjectID(&id)
|
objectAddress.SetObject(id)
|
||||||
|
|
||||||
var prm pool.PrmObjectGet
|
var prm pool.PrmObjectGet
|
||||||
prm.SetAddress(*objectAddress)
|
prm.SetAddress(objectAddress)
|
||||||
res, err := clientPool.GetObject(ctx, prm)
|
res, err := clientPool.GetObject(ctx, prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -451,14 +450,14 @@ func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID) {
|
func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID) {
|
||||||
content := []byte("some content")
|
content := []byte("some content")
|
||||||
attributes := map[string]string{
|
attributes := map[string]string{
|
||||||
object.AttributeFileName: "get-obj-name",
|
object.AttributeFileName: "get-obj-name",
|
||||||
"user-attribute": "user value",
|
"user-attribute": "user value",
|
||||||
}
|
}
|
||||||
|
|
||||||
objID := createObject(ctx, t, p, cnrID, attributes, content)
|
objID := createObject(ctx, t, p, ownerID, cnrID, attributes, content)
|
||||||
|
|
||||||
bearer := &models.Bearer{
|
bearer := &models.Bearer{
|
||||||
Object: []*models.Record{
|
Object: []*models.Record{
|
||||||
|
@ -491,16 +490,16 @@ func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.I
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.String()+"/"+objID.String()+"?"+query.Encode(), nil)
|
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/"+objID.EncodeToString()+"?"+query.Encode(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
|
||||||
objInfo := &models.ObjectInfo{}
|
objInfo := &models.ObjectInfo{}
|
||||||
doRequest(t, httpClient, request, http.StatusOK, objInfo)
|
doRequest(t, httpClient, request, http.StatusOK, objInfo)
|
||||||
|
|
||||||
require.Equal(t, cnrID.String(), *objInfo.ContainerID)
|
require.Equal(t, cnrID.EncodeToString(), *objInfo.ContainerID)
|
||||||
require.Equal(t, objID.String(), *objInfo.ObjectID)
|
require.Equal(t, objID.EncodeToString(), *objInfo.ObjectID)
|
||||||
require.Equal(t, p.OwnerID().String(), *objInfo.OwnerID)
|
require.Equal(t, ownerID.EncodeToString(), *objInfo.OwnerID)
|
||||||
require.Equal(t, len(attributes), len(objInfo.Attributes))
|
require.Equal(t, len(attributes), len(objInfo.Attributes))
|
||||||
require.Equal(t, int64(len(content)), *objInfo.ObjectSize)
|
require.Equal(t, int64(len(content)), *objInfo.ObjectSize)
|
||||||
|
|
||||||
|
@ -517,7 +516,7 @@ func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.I
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
query.Add("max-payload-size", "0")
|
query.Add("max-payload-size", "0")
|
||||||
|
|
||||||
request, err = http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.String()+"/"+objID.String()+"?"+query.Encode(), nil)
|
request, err = http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/"+objID.EncodeToString()+"?"+query.Encode(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
|
||||||
|
@ -533,7 +532,7 @@ func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.I
|
||||||
query.Add("range-offset", "0")
|
query.Add("range-offset", "0")
|
||||||
query.Add("range-length", strconv.Itoa(rangeLength))
|
query.Add("range-length", strconv.Itoa(rangeLength))
|
||||||
|
|
||||||
request, err = http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.String()+"/"+objID.String()+"?"+query.Encode(), nil)
|
request, err = http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/"+objID.EncodeToString()+"?"+query.Encode(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
|
||||||
|
@ -546,8 +545,8 @@ func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.I
|
||||||
require.Equal(t, content[:rangeLength], contentData)
|
require.Equal(t, content[:rangeLength], contentData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID) {
|
func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, owner *user.ID, cnrID cid.ID) {
|
||||||
objID := createObject(ctx, t, p, cnrID, nil, []byte("some content"))
|
objID := createObject(ctx, t, p, owner, cnrID, nil, []byte("some content"))
|
||||||
|
|
||||||
bearer := &models.Bearer{
|
bearer := &models.Bearer{
|
||||||
Object: []*models.Record{{
|
Object: []*models.Record{{
|
||||||
|
@ -569,7 +568,7 @@ func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *ci
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodDelete, testHost+"/v1/objects/"+cnrID.String()+"/"+objID.String()+"?"+query.Encode(), nil)
|
request, err := http.NewRequest(http.MethodDelete, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/"+objID.EncodeToString()+"?"+query.Encode(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
|
||||||
|
@ -577,9 +576,9 @@ func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *ci
|
||||||
doRequest(t, httpClient, request, http.StatusOK, resp)
|
doRequest(t, httpClient, request, http.StatusOK, resp)
|
||||||
require.True(t, *resp.Success)
|
require.True(t, *resp.Success)
|
||||||
|
|
||||||
var addr address.Address
|
var addr oid.Address
|
||||||
addr.SetContainerID(cnrID)
|
addr.SetContainer(cnrID)
|
||||||
addr.SetObjectID(objID)
|
addr.SetObject(objID)
|
||||||
|
|
||||||
var prm pool.PrmObjectHead
|
var prm pool.PrmObjectHead
|
||||||
prm.SetAddress(addr)
|
prm.SetAddress(addr)
|
||||||
|
@ -588,16 +587,16 @@ func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *ci
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func restObjectsSearch(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID) {
|
func restObjectsSearch(ctx context.Context, t *testing.T, p *pool.Pool, owner *user.ID, cnrID cid.ID) {
|
||||||
userKey, userValue := "User-Attribute", "user-attribute-value"
|
userKey, userValue := "User-Attribute", "user-attribute-value"
|
||||||
objectName := "object-name"
|
objectName := "object-name"
|
||||||
headers := map[string]string{
|
headers := map[string]string{
|
||||||
object.AttributeFileName: objectName,
|
object.AttributeFileName: objectName,
|
||||||
userKey: userValue,
|
userKey: userValue,
|
||||||
}
|
}
|
||||||
objID := createObject(ctx, t, p, cnrID, headers, []byte("some content"))
|
objID := createObject(ctx, t, p, owner, cnrID, headers, []byte("some content"))
|
||||||
headers[userKey] = "dummy"
|
headers[userKey] = "dummy"
|
||||||
_ = createObject(ctx, t, p, cnrID, headers, []byte("some content"))
|
_ = createObject(ctx, t, p, owner, cnrID, headers, []byte("some content"))
|
||||||
|
|
||||||
bearer := &models.Bearer{
|
bearer := &models.Bearer{
|
||||||
Object: []*models.Record{
|
Object: []*models.Record{
|
||||||
|
@ -643,7 +642,7 @@ func restObjectsSearch(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *c
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPost, testHost+"/v1/objects/"+cnrID.String()+"/search?"+query.Encode(), bytes.NewReader(body))
|
request, err := http.NewRequest(http.MethodPost, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/search?"+query.Encode(), bytes.NewReader(body))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
|
||||||
|
@ -654,8 +653,8 @@ func restObjectsSearch(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *c
|
||||||
require.Len(t, resp.Objects, 1)
|
require.Len(t, resp.Objects, 1)
|
||||||
|
|
||||||
objBaseInfo := resp.Objects[0]
|
objBaseInfo := resp.Objects[0]
|
||||||
require.Equal(t, cnrID.String(), *objBaseInfo.Address.ContainerID)
|
require.Equal(t, cnrID.EncodeToString(), *objBaseInfo.Address.ContainerID)
|
||||||
require.Equal(t, objID.String(), *objBaseInfo.Address.ObjectID)
|
require.Equal(t, objID.EncodeToString(), *objBaseInfo.Address.ObjectID)
|
||||||
require.Equal(t, objectName, objBaseInfo.Name)
|
require.Equal(t, objectName, objBaseInfo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,21 +681,21 @@ func doRequest(t *testing.T, httpClient *http.Client, request *http.Request, exp
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func restContainerGet(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID *cid.ID) {
|
func restContainerGet(ctx context.Context, t *testing.T, owner user.ID, cnrID cid.ID) {
|
||||||
httpClient := &http.Client{Timeout: 5 * time.Second}
|
httpClient := &http.Client{Timeout: 5 * time.Second}
|
||||||
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/containers/"+cnrID.String(), nil)
|
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/containers/"+cnrID.EncodeToString(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
request = request.WithContext(ctx)
|
request = request.WithContext(ctx)
|
||||||
|
|
||||||
cnrInfo := &models.ContainerInfo{}
|
cnrInfo := &models.ContainerInfo{}
|
||||||
doRequest(t, httpClient, request, http.StatusOK, cnrInfo)
|
doRequest(t, httpClient, request, http.StatusOK, cnrInfo)
|
||||||
|
|
||||||
require.Equal(t, cnrID.String(), *cnrInfo.ContainerID)
|
require.Equal(t, cnrID.EncodeToString(), *cnrInfo.ContainerID)
|
||||||
require.Equal(t, clientPool.OwnerID().String(), *cnrInfo.OwnerID)
|
require.Equal(t, owner.EncodeToString(), *cnrInfo.OwnerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Pool) {
|
func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Pool, owner user.ID) {
|
||||||
cnrID := createContainer(ctx, t, clientPool, "for-delete")
|
cnrID := createContainer(ctx, t, clientPool, owner, "for-delete")
|
||||||
|
|
||||||
bearer := &models.Bearer{
|
bearer := &models.Bearer{
|
||||||
Container: &models.Rule{
|
Container: &models.Rule{
|
||||||
|
@ -711,7 +710,7 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodDelete, testHost+"/v1/containers/"+cnrID.String()+"?"+query.Encode(), nil)
|
request, err := http.NewRequest(http.MethodDelete, testHost+"/v1/containers/"+cnrID.EncodeToString()+"?"+query.Encode(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
request = request.WithContext(ctx)
|
request = request.WithContext(ctx)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
@ -721,15 +720,15 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
|
||||||
require.True(t, *resp.Success)
|
require.True(t, *resp.Success)
|
||||||
|
|
||||||
var prm pool.PrmContainerGet
|
var prm pool.PrmContainerGet
|
||||||
prm.SetContainerID(*cnrID)
|
prm.SetContainerID(cnrID)
|
||||||
|
|
||||||
_, err = clientPool.GetContainer(ctx, prm)
|
_, err = clientPool.GetContainer(ctx, prm)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Contains(t, err.Error(), "not found")
|
require.Contains(t, err.Error(), "not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Pool) {
|
func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, owner user.ID) {
|
||||||
cnrID := createContainer(ctx, t, clientPool, "for-eacl-put")
|
cnrID := createContainer(ctx, t, clientPool, owner, "for-eacl-put")
|
||||||
httpClient := &http.Client{Timeout: 60 * time.Second}
|
httpClient := &http.Client{Timeout: 60 * time.Second}
|
||||||
bearer := &models.Bearer{
|
bearer := &models.Bearer{
|
||||||
Container: &models.Rule{
|
Container: &models.Rule{
|
||||||
|
@ -757,7 +756,7 @@ func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Po
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPut, testHost+"/v1/containers/"+cnrID.String()+"/eacl?"+query.Encode(), bytes.NewReader(body))
|
request, err := http.NewRequest(http.MethodPut, testHost+"/v1/containers/"+cnrID.EncodeToString()+"/eacl?"+query.Encode(), bytes.NewReader(body))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
request = request.WithContext(ctx)
|
request = request.WithContext(ctx)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
@ -767,7 +766,7 @@ func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Po
|
||||||
require.True(t, *resp.Success)
|
require.True(t, *resp.Success)
|
||||||
|
|
||||||
var prm pool.PrmContainerEACL
|
var prm pool.PrmContainerEACL
|
||||||
prm.SetContainerID(*cnrID)
|
prm.SetContainerID(cnrID)
|
||||||
|
|
||||||
table, err := clientPool.GetEACL(ctx, prm)
|
table, err := clientPool.GetEACL(ctx, prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -779,22 +778,22 @@ func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Po
|
||||||
require.True(t, eacl.EqualTables(*expectedTable, *table))
|
require.True(t, eacl.EqualTables(*expectedTable, *table))
|
||||||
}
|
}
|
||||||
|
|
||||||
func restContainerEACLGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID) {
|
func restContainerEACLGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID cid.ID) {
|
||||||
var prm pool.PrmContainerEACL
|
var prm pool.PrmContainerEACL
|
||||||
prm.SetContainerID(*cnrID)
|
prm.SetContainerID(cnrID)
|
||||||
expectedTable, err := p.GetEACL(ctx, prm)
|
expectedTable, err := p.GetEACL(ctx, prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
httpClient := &http.Client{Timeout: 60 * time.Second}
|
httpClient := &http.Client{Timeout: 60 * time.Second}
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/containers/"+cnrID.String()+"/eacl", nil)
|
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/containers/"+cnrID.EncodeToString()+"/eacl", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
request = request.WithContext(ctx)
|
request = request.WithContext(ctx)
|
||||||
|
|
||||||
responseTable := &models.Eacl{}
|
responseTable := &models.Eacl{}
|
||||||
doRequest(t, httpClient, request, http.StatusOK, responseTable)
|
doRequest(t, httpClient, request, http.StatusOK, responseTable)
|
||||||
|
|
||||||
require.Equal(t, cnrID.String(), responseTable.ContainerID)
|
require.Equal(t, cnrID.EncodeToString(), responseTable.ContainerID)
|
||||||
|
|
||||||
actualTable, err := util.ToNativeTable(responseTable.Records)
|
actualTable, err := util.ToNativeTable(responseTable.Records)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -803,9 +802,9 @@ func restContainerEACLGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID
|
||||||
require.True(t, eacl.EqualTables(*expectedTable, *actualTable))
|
require.True(t, eacl.EqualTables(*expectedTable, *actualTable))
|
||||||
}
|
}
|
||||||
|
|
||||||
func restContainerList(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID) {
|
func restContainerList(ctx context.Context, t *testing.T, p *pool.Pool, owner user.ID, cnrID cid.ID) {
|
||||||
var prm pool.PrmContainerList
|
var prm pool.PrmContainerList
|
||||||
prm.SetOwnerID(*p.OwnerID())
|
prm.SetOwnerID(owner)
|
||||||
|
|
||||||
ids, err := p.ListContainers(ctx, prm)
|
ids, err := p.ListContainers(ctx, prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -813,7 +812,7 @@ func restContainerList(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *c
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
|
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add("ownerId", p.OwnerID().String())
|
query.Add("ownerId", owner.EncodeToString())
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/containers?"+query.Encode(), nil)
|
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/containers?"+query.Encode(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -824,14 +823,14 @@ func restContainerList(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *c
|
||||||
|
|
||||||
require.Equal(t, len(ids), int(*list.Size))
|
require.Equal(t, len(ids), int(*list.Size))
|
||||||
|
|
||||||
require.Truef(t, containsContainer(list.Containers, cnrID.String(), containerName), "list doesn't contain cnr '%s' with name '%s'", cnrID.String(), containerName)
|
require.Truef(t, containsContainer(list.Containers, cnrID.EncodeToString(), containerName), "list doesn't contain cnr '%s' with name '%s'", cnrID.EncodeToString(), containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func containsContainer(containers []*models.ContainerInfo, cnrID, cnrName string) bool {
|
func containsContainer(containers []*models.ContainerInfo, cnrID, cnrName string) bool {
|
||||||
for _, cnrInfo := range containers {
|
for _, cnrInfo := range containers {
|
||||||
if *cnrInfo.ContainerID == cnrID {
|
if *cnrInfo.ContainerID == cnrID {
|
||||||
for _, attr := range cnrInfo.Attributes {
|
for _, attr := range cnrInfo.Attributes {
|
||||||
if *attr.Key == container.AttributeName && *attr.Value == cnrName {
|
if *attr.Key == "Name" && *attr.Value == cnrName {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -848,7 +847,8 @@ func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearers []*models.B
|
||||||
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
|
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ownerID := owner.NewIDFromPublicKey((*ecdsa.PublicKey)(key.PublicKey()))
|
var ownerID user.ID
|
||||||
|
user.IDFromKey(&ownerID, key.PrivateKey.PublicKey)
|
||||||
|
|
||||||
data, err := json.Marshal(bearers)
|
data, err := json.Marshal(bearers)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -924,14 +924,13 @@ func signToken(t *testing.T, key *keys.PrivateKey, data []byte) *handlers.Bearer
|
||||||
}
|
}
|
||||||
|
|
||||||
func signTokenWalletConnect(t *testing.T, key *keys.PrivateKey, data []byte) *handlers.BearerToken {
|
func signTokenWalletConnect(t *testing.T, key *keys.PrivateKey, data []byte) *handlers.BearerToken {
|
||||||
b64Token := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
|
signer := neofsecdsa.SignerWalletConnect(key.PrivateKey)
|
||||||
base64.StdEncoding.Encode(b64Token, data)
|
signature, err := signer.Sign(data)
|
||||||
sm, err := walletconnect.SignMessage(&key.PrivateKey, b64Token[:])
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return &handlers.BearerToken{
|
return &handlers.BearerToken{
|
||||||
Token: string(b64Token),
|
Token: base64.StdEncoding.EncodeToString(data),
|
||||||
Signature: hex.EncodeToString(append(sm.Data, sm.Salt...)),
|
Signature: hex.EncodeToString(signature),
|
||||||
Key: hex.EncodeToString(key.PublicKey().Bytes()),
|
Key: hex.EncodeToString(key.PublicKey().Bytes()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1018,7 +1017,7 @@ func restContainerPut(ctx context.Context, t *testing.T, clientPool *pool.Pool)
|
||||||
doRequest(t, httpClient, request, http.StatusOK, addr)
|
doRequest(t, httpClient, request, http.StatusOK, addr)
|
||||||
|
|
||||||
var CID cid.ID
|
var CID cid.ID
|
||||||
err = CID.Parse(*addr.ContainerID)
|
err = CID.DecodeString(*addr.ContainerID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
fmt.Println(CID.String())
|
fmt.Println(CID.String())
|
||||||
|
|
||||||
|
@ -1028,10 +1027,10 @@ func restContainerPut(ctx context.Context, t *testing.T, clientPool *pool.Pool)
|
||||||
cnr, err := clientPool.GetContainer(ctx, prm)
|
cnr, err := clientPool.GetContainer(ctx, prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cnrAttr := make(map[string]string, len(cnr.Attributes()))
|
cnrAttr := make(map[string]string)
|
||||||
for _, attribute := range cnr.Attributes() {
|
cnr.IterateAttributes(func(key, val string) {
|
||||||
cnrAttr[attribute.Key()] = attribute.Value()
|
cnrAttr[key] = val
|
||||||
}
|
})
|
||||||
|
|
||||||
for key, val := range userAttributes {
|
for key, val := range userAttributes {
|
||||||
require.Equal(t, val, cnrAttr[key])
|
require.Equal(t, val, cnrAttr[key])
|
||||||
|
@ -1045,32 +1044,38 @@ func prepareCommonHeaders(header http.Header, bearerToken *handlers.BearerToken)
|
||||||
header.Add(XBearerSignatureKey, bearerToken.Key)
|
header.Add(XBearerSignatureKey, bearerToken.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, name string) *cid.ID {
|
func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, owner user.ID, name string) cid.ID {
|
||||||
pp, err := policy.Parse("REP 1")
|
var policy netmap.PlacementPolicy
|
||||||
|
err := policy.DecodeString("REP 1")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cnr := container.New(
|
var cnr container.Container
|
||||||
container.WithPolicy(pp),
|
cnr.Init()
|
||||||
container.WithCustomBasicACL(0x0FFFFFFF),
|
cnr.SetOwner(owner)
|
||||||
container.WithAttribute(container.AttributeName, name),
|
cnr.SetPlacementPolicy(policy)
|
||||||
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)))
|
cnr.SetBasicACL(acl.PublicRWExtended)
|
||||||
cnr.SetOwnerID(clientPool.OwnerID())
|
|
||||||
|
container.SetName(&cnr, name)
|
||||||
|
container.SetCreationTime(&cnr, time.Now())
|
||||||
|
|
||||||
|
err = pool.SyncContainerWithNetwork(ctx, &cnr, clientPool)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
var waitPrm pool.WaitParams
|
var waitPrm pool.WaitParams
|
||||||
waitPrm.SetPollInterval(3 * time.Second)
|
waitPrm.SetPollInterval(3 * time.Second)
|
||||||
waitPrm.SetTimeout(15 * time.Second)
|
waitPrm.SetTimeout(15 * time.Second)
|
||||||
|
|
||||||
var prm pool.PrmContainerPut
|
var prm pool.PrmContainerPut
|
||||||
prm.SetContainer(*cnr)
|
prm.SetContainer(cnr)
|
||||||
prm.SetWaitParams(waitPrm)
|
prm.SetWaitParams(waitPrm)
|
||||||
|
|
||||||
CID, err := clientPool.PutContainer(ctx, prm)
|
CID, err := clientPool.PutContainer(ctx, prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return CID
|
return *CID
|
||||||
}
|
}
|
||||||
|
|
||||||
func createObject(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID, headers map[string]string, payload []byte) *oid.ID {
|
func createObject(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID, headers map[string]string, payload []byte) oid.ID {
|
||||||
attributes := make([]object.Attribute, 0, len(headers))
|
attributes := make([]object.Attribute, 0, len(headers))
|
||||||
|
|
||||||
for key, val := range headers {
|
for key, val := range headers {
|
||||||
|
@ -1081,7 +1086,7 @@ func createObject(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
obj := object.New()
|
obj := object.New()
|
||||||
obj.SetOwnerID(p.OwnerID())
|
obj.SetOwnerID(ownerID)
|
||||||
obj.SetContainerID(cnrID)
|
obj.SetContainerID(cnrID)
|
||||||
obj.SetAttributes(attributes...)
|
obj.SetAttributes(attributes...)
|
||||||
obj.SetPayload(payload)
|
obj.SetPayload(payload)
|
||||||
|
@ -1092,10 +1097,10 @@ func createObject(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID
|
||||||
objID, err := p.PutObject(ctx, prm)
|
objID, err := p.PutObject(ctx, prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return objID
|
return *objID
|
||||||
}
|
}
|
||||||
|
|
||||||
func restrictByEACL(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID *cid.ID) *eacl.Table {
|
func restrictByEACL(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID cid.ID) *eacl.Table {
|
||||||
table := eacl.NewTable()
|
table := eacl.NewTable()
|
||||||
table.SetCID(cnrID)
|
table.SetCID(cnrID)
|
||||||
|
|
||||||
|
|
7
go.mod
7
go.mod
|
@ -12,8 +12,8 @@ require (
|
||||||
github.com/go-openapi/validate v0.21.0
|
github.com/go-openapi/validate v0.21.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/nspcc-dev/neo-go v0.98.2
|
github.com/nspcc-dev/neo-go v0.98.2
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.12.1
|
github.com/nspcc-dev/neofs-api-go/v2 v2.13.0
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.3.0.20220412151250-3e75660802ae
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.5
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/testcontainers/testcontainers-go v0.13.0
|
github.com/testcontainers/testcontainers-go v0.13.0
|
||||||
|
@ -64,8 +64,9 @@ require (
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/nspcc-dev/hrw v1.0.9 // indirect
|
github.com/nspcc-dev/hrw v1.0.9 // indirect
|
||||||
github.com/nspcc-dev/neofs-crypto v0.3.0
|
github.com/nspcc-dev/neofs-crypto v0.3.0 // indirect
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
||||||
|
github.com/nspcc-dev/tzhash v1.5.2 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
|
|
12
go.sum
12
go.sum
|
@ -742,19 +742,22 @@ github.com/nspcc-dev/neo-go v0.98.2/go.mod h1:KXKqJwfTyVJzDarSCDqFaKrVbg/qz0ZBk2
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220321113211-526c423a6152/go.mod h1:QBE0I30F2kOAISNpT5oks82yF4wkkUq3SCfI3Hqgx/Y=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220321113211-526c423a6152/go.mod h1:QBE0I30F2kOAISNpT5oks82yF4wkkUq3SCfI3Hqgx/Y=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.12.1 h1:PVU2rLlG9S0jDe5eKyaUs4nKo/la+mN5pvz32Gib3qM=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.13.0 h1:7BcBiSjmWqJx0SPFlGRYt9ZFbMjucRIz9+Mv8UBLeq8=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.12.1/go.mod h1:73j09Xa7I2zQbM3HCvAHnDHPYiiWnEHa1d6Z6RDMBLU=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.13.0/go.mod h1:73j09Xa7I2zQbM3HCvAHnDHPYiiWnEHa1d6Z6RDMBLU=
|
||||||
|
github.com/nspcc-dev/neofs-contract v0.15.1/go.mod h1:kxO5ZTqdzFnRM5RMvM+Fhd+3GGrJo6AmG2ZyA9OCqqQ=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
|
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
|
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4=
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40=
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.3.0.20220412151250-3e75660802ae h1:xcoEwEwZXu784Re1PPE35vm1A4+sUCVgMuGFqQPnN1Q=
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.5 h1:PSUUUL0XloTQdAgaI0WIY54TsqQ0GpxehHC7FaTkHvI=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.3.0.20220412151250-3e75660802ae/go.mod h1:Hl7a1l0ntZ4b1ZABpGX6fuAuFS3c6+hyMCUNVvZv/w4=
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.5/go.mod h1:hP3HrbK8omERJZvwjsGZgvzsUDxsPDPemrHzgqfpADM=
|
||||||
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
|
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||||
|
github.com/nspcc-dev/tzhash v1.5.2 h1:GuIQPOY2xpl5ZE1pbUbz+QdKXVOTyzbbxSVv0nBfa98=
|
||||||
|
github.com/nspcc-dev/tzhash v1.5.2/go.mod h1:gwAx6mcsbkfY+JVp+PovoP2Gvw6y57W8dj7zDHKOhzI=
|
||||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
|
@ -1291,6 +1294,7 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb h1:PVGECzEo9Y3uOidtkHGdd347NjLtITfJFO9BxFpmRoo=
|
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb h1:PVGECzEo9Y3uOidtkHGdd347NjLtITfJFO9BxFpmRoo=
|
||||||
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ type API struct {
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
pool *pool.Pool
|
pool *pool.Pool
|
||||||
key *keys.PrivateKey
|
key *keys.PrivateKey
|
||||||
|
owner *user.ID
|
||||||
defaultTimestamp bool
|
defaultTimestamp bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,10 +61,14 @@ const (
|
||||||
|
|
||||||
// New creates a new API using specified logger, connection pool and other parameters.
|
// New creates a new API using specified logger, connection pool and other parameters.
|
||||||
func New(prm *PrmAPI) *API {
|
func New(prm *PrmAPI) *API {
|
||||||
|
var owner user.ID
|
||||||
|
user.IDFromKey(&owner, prm.Key.PrivateKey.PublicKey)
|
||||||
|
|
||||||
return &API{
|
return &API{
|
||||||
log: prm.Logger,
|
log: prm.Logger,
|
||||||
pool: prm.Pool,
|
pool: prm.Pool,
|
||||||
key: prm.Key,
|
key: prm.Key,
|
||||||
|
owner: &owner,
|
||||||
defaultTimestamp: prm.DefaultTimestamp,
|
defaultTimestamp: prm.DefaultTimestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,15 @@ import (
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
|
sessionv2 "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultTokenExpDuration = 100 // in epoch
|
const defaultTokenExpDuration = 100 // in epoch
|
||||||
|
@ -83,7 +87,7 @@ func (a *API) PostAuth(params operations.AuthParams) middleware.Responder {
|
||||||
|
|
||||||
if isObject {
|
if isObject {
|
||||||
prm := newObjectParams(commonPrm, token)
|
prm := newObjectParams(commonPrm, token)
|
||||||
response[i], err = prepareObjectToken(ctx, prm, a.pool)
|
response[i], err = prepareObjectToken(ctx, prm, a.pool, *a.owner)
|
||||||
} else {
|
} else {
|
||||||
prm := newContainerParams(commonPrm, token)
|
prm := newContainerParams(commonPrm, token)
|
||||||
response[i], err = prepareContainerTokens(ctx, prm, a.pool, a.key.PublicKey())
|
response[i], err = prepareContainerTokens(ctx, prm, a.pool, a.key.PublicKey())
|
||||||
|
@ -96,23 +100,23 @@ func (a *API) PostAuth(params operations.AuthParams) middleware.Responder {
|
||||||
return operations.NewAuthOK().WithPayload(response)
|
return operations.NewAuthOK().WithPayload(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareObjectToken(ctx context.Context, params objectTokenParams, pool *pool.Pool) (*models.TokenResponse, error) {
|
func prepareObjectToken(ctx context.Context, params objectTokenParams, pool *pool.Pool, owner user.ID) (*models.TokenResponse, error) {
|
||||||
btoken, err := util.ToNativeObjectToken(params.Records)
|
btoken, err := util.ToNativeObjectToken(params.Records)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't transform token to native: %w", err)
|
return nil, fmt.Errorf("couldn't transform token to native: %w", err)
|
||||||
}
|
}
|
||||||
btoken.SetOwner(pool.OwnerID())
|
btoken.ForUser(owner)
|
||||||
|
|
||||||
iat, exp, err := getTokenLifetime(ctx, pool, params.XBearerLifetime)
|
iat, exp, err := getTokenLifetime(ctx, pool, params.XBearerLifetime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't get lifetime: %w", err)
|
return nil, fmt.Errorf("couldn't get lifetime: %w", err)
|
||||||
}
|
}
|
||||||
btoken.SetLifetime(exp, 0, iat)
|
btoken.SetIat(iat)
|
||||||
|
btoken.SetExp(exp)
|
||||||
|
|
||||||
binaryBearer, err := btoken.ToV2().GetBody().StableMarshal(nil)
|
var v2token acl.BearerToken
|
||||||
if err != nil {
|
btoken.WriteToV2(&v2token)
|
||||||
return nil, fmt.Errorf("couldn't marshal bearer token: %w", err)
|
binaryBearer := v2token.GetBody().StableMarshal(nil)
|
||||||
}
|
|
||||||
|
|
||||||
return &models.TokenResponse{
|
return &models.TokenResponse{
|
||||||
Name: params.Name,
|
Name: params.Name,
|
||||||
|
@ -127,8 +131,8 @@ func prepareContainerTokens(ctx context.Context, params containerTokenParams, po
|
||||||
return nil, fmt.Errorf("couldn't get lifetime: %w", err)
|
return nil, fmt.Errorf("couldn't get lifetime: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ownerID owner.ID
|
var ownerID user.ID
|
||||||
if err = ownerID.Parse(params.XBearerOwnerID); err != nil {
|
if err = ownerID.DecodeString(params.XBearerOwnerID); err != nil {
|
||||||
return nil, fmt.Errorf("invalid bearer owner: %w", err)
|
return nil, fmt.Errorf("invalid bearer owner: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,22 +141,21 @@ func prepareContainerTokens(ctx context.Context, params containerTokenParams, po
|
||||||
return nil, fmt.Errorf("couldn't transform rule to native session token: %w", err)
|
return nil, fmt.Errorf("couldn't transform rule to native session token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, err := uuid.New().MarshalBinary()
|
stoken.SetID(uuid.New())
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
stoken.SetID(uid)
|
|
||||||
|
|
||||||
stoken.SetOwnerID(&ownerID)
|
|
||||||
|
|
||||||
stoken.SetIat(iat)
|
stoken.SetIat(iat)
|
||||||
stoken.SetExp(exp)
|
stoken.SetExp(exp)
|
||||||
stoken.SetSessionKey(key.Bytes())
|
|
||||||
|
|
||||||
binaryToken, err := stoken.ToV2().GetBody().StableMarshal(nil)
|
authKey := neofsecdsa.PublicKey(*key)
|
||||||
if err != nil {
|
stoken.SetAuthKey(&authKey)
|
||||||
return nil, fmt.Errorf("couldn't marshal session token: %w", err)
|
|
||||||
}
|
var v2token sessionv2.Token
|
||||||
|
stoken.WriteToV2(&v2token)
|
||||||
|
|
||||||
|
var issuer refs.OwnerID
|
||||||
|
ownerID.WriteToV2(&issuer)
|
||||||
|
v2token.GetBody().SetOwnerID(&issuer)
|
||||||
|
|
||||||
|
binaryToken := v2token.GetBody().StableMarshal(nil)
|
||||||
|
|
||||||
return &models.TokenResponse{
|
return &models.TokenResponse{
|
||||||
Name: params.Name,
|
Name: params.Name,
|
||||||
|
|
|
@ -7,12 +7,14 @@ import (
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,14 +39,19 @@ func TestSign(t *testing.T) {
|
||||||
btoken, err := util.ToNativeObjectToken(records)
|
btoken, err := util.ToNativeObjectToken(records)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
btoken.SetExp(math.MaxInt64)
|
||||||
|
|
||||||
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)
|
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
btoken.SetOwner(owner.NewIDFromPublicKey((*ecdsa.PublicKey)(ownerKey)))
|
var owner user.ID
|
||||||
|
user.IDFromKey(&owner, *(*ecdsa.PublicKey)(ownerKey))
|
||||||
|
btoken.ForUser(owner)
|
||||||
|
|
||||||
binaryBearer, err := btoken.ToV2().GetBody().StableMarshal(nil)
|
var v2token acl.BearerToken
|
||||||
require.NoError(t, err)
|
btoken.WriteToV2(&v2token)
|
||||||
|
|
||||||
|
binaryBearer := v2token.GetBody().StableMarshal(nil)
|
||||||
bearerBase64 := base64.StdEncoding.EncodeToString(binaryBearer)
|
bearerBase64 := base64.StdEncoding.EncodeToString(binaryBearer)
|
||||||
|
|
||||||
h := sha512.Sum512(binaryBearer)
|
h := sha512.Sum512(binaryBearer)
|
||||||
|
|
|
@ -2,13 +2,11 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -19,20 +17,21 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
||||||
walletconnect "github.com/nspcc-dev/neofs-rest-gw/internal/wallet-connect"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/acl"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/container"
|
"github.com/nspcc-dev/neofs-sdk-go/container"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/policy"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultPlacementPolicy = "REP 3"
|
defaultPlacementPolicy = "REP 3"
|
||||||
defaultBasicACL = acl.PrivateBasicName
|
defaultBasicACL = acl.NamePrivate
|
||||||
|
attributeName = "Name"
|
||||||
|
attributeTimestamp = "Timestamp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PutContainers handler that creates container in NeoFS.
|
// PutContainers handler that creates container in NeoFS.
|
||||||
|
@ -60,7 +59,7 @@ func (a *API) PutContainers(params operations.PutContainerParams, principal *mod
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp operations.PutContainerOKBody
|
var resp operations.PutContainerOKBody
|
||||||
resp.ContainerID = util.NewString(cnrID.String())
|
resp.ContainerID = util.NewString(cnrID.EncodeToString())
|
||||||
|
|
||||||
return operations.NewPutContainerOK().WithPayload(&resp)
|
return operations.NewPutContainerOK().WithPayload(&resp)
|
||||||
}
|
}
|
||||||
|
@ -73,7 +72,7 @@ func (a *API) GetContainer(params operations.GetContainerParams) middleware.Resp
|
||||||
return operations.NewGetContainerBadRequest().WithPayload(resp)
|
return operations.NewGetContainerBadRequest().WithPayload(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
cnrInfo, err := getContainerInfo(params.HTTPRequest.Context(), a.pool, *cnrID)
|
cnrInfo, err := getContainerInfo(params.HTTPRequest.Context(), a.pool, cnrID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp := a.logAndGetErrorResponse("get container", err)
|
resp := a.logAndGetErrorResponse("get container", err)
|
||||||
return operations.NewGetContainerBadRequest().WithPayload(resp)
|
return operations.NewGetContainerBadRequest().WithPayload(resp)
|
||||||
|
@ -133,8 +132,8 @@ func (a *API) GetContainerEACL(params operations.GetContainerEACLParams) middlew
|
||||||
func (a *API) ListContainer(params operations.ListContainersParams) middleware.Responder {
|
func (a *API) ListContainer(params operations.ListContainersParams) middleware.Responder {
|
||||||
ctx := params.HTTPRequest.Context()
|
ctx := params.HTTPRequest.Context()
|
||||||
|
|
||||||
var ownerID owner.ID
|
var ownerID user.ID
|
||||||
if err := ownerID.Parse(params.OwnerID); err != nil {
|
if err := ownerID.DecodeString(params.OwnerID); err != nil {
|
||||||
resp := a.logAndGetErrorResponse("invalid owner id", err)
|
resp := a.logAndGetErrorResponse("invalid owner id", err)
|
||||||
return operations.NewListContainersBadRequest().WithPayload(resp)
|
return operations.NewListContainersBadRequest().WithPayload(resp)
|
||||||
}
|
}
|
||||||
|
@ -203,8 +202,8 @@ func (a *API) DeleteContainer(params operations.DeleteContainerParams, principal
|
||||||
}
|
}
|
||||||
|
|
||||||
var prm pool.PrmContainerDelete
|
var prm pool.PrmContainerDelete
|
||||||
prm.SetContainerID(*cnrID)
|
prm.SetContainerID(cnrID)
|
||||||
prm.SetSessionToken(*stoken)
|
prm.SetSessionToken(stoken)
|
||||||
|
|
||||||
if err = a.pool.DeleteContainer(params.HTTPRequest.Context(), prm); err != nil {
|
if err = a.pool.DeleteContainer(params.HTTPRequest.Context(), prm); err != nil {
|
||||||
resp := a.logAndGetErrorResponse("delete container", err, zap.String("container", params.ContainerID))
|
resp := a.logAndGetErrorResponse("delete container", err, zap.String("container", params.ContainerID))
|
||||||
|
@ -223,58 +222,62 @@ func getContainerInfo(ctx context.Context, p *pool.Pool, cnrID cid.ID) (*models.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs := make([]*models.Attribute, len(cnr.Attributes()))
|
var attrs []*models.Attribute
|
||||||
for i, attr := range cnr.Attributes() {
|
cnr.IterateAttributes(func(key, val string) {
|
||||||
attrs[i] = &models.Attribute{
|
attrs = append(attrs, &models.Attribute{
|
||||||
Key: util.NewString(attr.Key()),
|
Key: util.NewString(key),
|
||||||
Value: util.NewString(attr.Value()),
|
Value: util.NewString(val),
|
||||||
}
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
if err = cnr.PlacementPolicy().WriteStringTo(&sb); err != nil {
|
||||||
|
return nil, fmt.Errorf("writer policy to string: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &models.ContainerInfo{
|
return &models.ContainerInfo{
|
||||||
ContainerID: util.NewString(cnrID.String()),
|
ContainerID: util.NewString(cnrID.String()),
|
||||||
Version: util.NewString(cnr.Version().String()),
|
OwnerID: util.NewString(cnr.Owner().String()),
|
||||||
OwnerID: util.NewString(cnr.OwnerID().String()),
|
BasicACL: util.NewString(cnr.BasicACL().EncodeToString()),
|
||||||
BasicACL: util.NewString(acl.BasicACL(cnr.BasicACL()).String()),
|
PlacementPolicy: util.NewString(sb.String()),
|
||||||
PlacementPolicy: util.NewString(strings.Join(policy.Encode(cnr.PlacementPolicy()), " ")),
|
|
||||||
Attributes: attrs,
|
Attributes: attrs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareUserAttributes(header http.Header) map[string]string {
|
func prepareUserAttributes(header http.Header) map[string]string {
|
||||||
filtered := filterHeaders(header)
|
filtered := filterHeaders(header)
|
||||||
delete(filtered, container.AttributeName)
|
delete(filtered, attributeName)
|
||||||
delete(filtered, container.AttributeTimestamp)
|
delete(filtered, attributeTimestamp)
|
||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseContainerID(containerID string) (*cid.ID, error) {
|
func parseContainerID(containerID string) (cid.ID, error) {
|
||||||
var cnrID cid.ID
|
var cnrID cid.ID
|
||||||
if err := cnrID.Parse(containerID); err != nil {
|
if err := cnrID.DecodeString(containerID); err != nil {
|
||||||
return nil, fmt.Errorf("parse container id '%s': %w", containerID, err)
|
return cid.ID{}, fmt.Errorf("parse container id '%s': %w", containerID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &cnrID, nil
|
return cnrID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setContainerEACL(ctx context.Context, p *pool.Pool, cnrID *cid.ID, stoken *session.Token, eaclPrm *models.Eacl) error {
|
func setContainerEACL(ctx context.Context, p *pool.Pool, cnrID cid.ID, stoken session.Container, eaclPrm *models.Eacl) error {
|
||||||
table, err := util.ToNativeTable(eaclPrm.Records)
|
table, err := util.ToNativeTable(eaclPrm.Records)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
table.SetCID(cnrID)
|
table.SetCID(cnrID)
|
||||||
table.SetSessionToken(stoken)
|
|
||||||
|
|
||||||
var prm pool.PrmContainerSetEACL
|
var prm pool.PrmContainerSetEACL
|
||||||
prm.SetTable(*table)
|
prm.SetTable(*table)
|
||||||
|
prm.WithinSession(stoken)
|
||||||
|
|
||||||
return p.SetEACL(ctx, prm)
|
return p.SetEACL(ctx, prm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContainerEACL(ctx context.Context, p *pool.Pool, cnrID *cid.ID) (*models.Eacl, error) {
|
func getContainerEACL(ctx context.Context, p *pool.Pool, cnrID cid.ID) (*models.Eacl, error) {
|
||||||
var prm pool.PrmContainerEACL
|
var prm pool.PrmContainerEACL
|
||||||
prm.SetContainerID(*cnrID)
|
prm.SetContainerID(cnrID)
|
||||||
|
|
||||||
table, err := p.GetEACL(ctx, prm)
|
table, err := p.GetEACL(ctx, prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -282,7 +285,7 @@ func getContainerEACL(ctx context.Context, p *pool.Pool, cnrID *cid.ID) (*models
|
||||||
}
|
}
|
||||||
|
|
||||||
tableResp := &models.Eacl{
|
tableResp := &models.Eacl{
|
||||||
ContainerID: cnrID.String(),
|
ContainerID: cnrID.EncodeToString(),
|
||||||
Records: make([]*models.Record, len(table.Records())),
|
Records: make([]*models.Record, len(table.Records())),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,59 +300,63 @@ func getContainerEACL(ctx context.Context, p *pool.Pool, cnrID *cid.ID) (*models
|
||||||
return tableResp, nil
|
return tableResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createContainer(ctx context.Context, p *pool.Pool, stoken *session.Token, params *operations.PutContainerParams, userAttrs map[string]string) (*cid.ID, error) {
|
func createContainer(ctx context.Context, p *pool.Pool, stoken session.Container, params *operations.PutContainerParams, userAttrs map[string]string) (cid.ID, error) {
|
||||||
request := params.Container
|
request := params.Container
|
||||||
|
|
||||||
if request.PlacementPolicy == "" {
|
if request.PlacementPolicy == "" {
|
||||||
request.PlacementPolicy = defaultPlacementPolicy
|
request.PlacementPolicy = defaultPlacementPolicy
|
||||||
}
|
}
|
||||||
pp, err := policy.Parse(request.PlacementPolicy)
|
var policy netmap.PlacementPolicy
|
||||||
|
err := policy.DecodeString(request.PlacementPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't parse placement policy: %w", err)
|
return cid.ID{}, fmt.Errorf("couldn't parse placement policy: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.BasicACL == "" {
|
if request.BasicACL == "" {
|
||||||
request.BasicACL = defaultBasicACL
|
request.BasicACL = defaultBasicACL
|
||||||
}
|
}
|
||||||
basicACL, err := acl.ParseBasicACL(request.BasicACL)
|
|
||||||
if err != nil {
|
var basicACL acl.Basic
|
||||||
return nil, fmt.Errorf("couldn't parse basic acl: %w", err)
|
if err = basicACL.DecodeString(request.BasicACL); err != nil {
|
||||||
|
return cid.ID{}, fmt.Errorf("couldn't parse basic acl: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cnrOptions := []container.Option{
|
var cnr container.Container
|
||||||
container.WithPolicy(pp),
|
cnr.Init()
|
||||||
container.WithCustomBasicACL(basicACL),
|
cnr.SetPlacementPolicy(policy)
|
||||||
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)),
|
cnr.SetBasicACL(basicACL)
|
||||||
}
|
cnr.SetOwner(stoken.Issuer())
|
||||||
|
|
||||||
|
container.SetCreationTime(&cnr, time.Now())
|
||||||
|
|
||||||
if request.ContainerName != "" {
|
if request.ContainerName != "" {
|
||||||
container.WithAttribute(container.AttributeName, request.ContainerName)
|
container.SetName(&cnr, request.ContainerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, val := range userAttrs {
|
for key, val := range userAttrs {
|
||||||
cnrOptions = append(cnrOptions, container.WithAttribute(key, val))
|
cnr.SetAttribute(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
cnr := container.New(cnrOptions...)
|
|
||||||
cnr.SetOwnerID(stoken.OwnerID())
|
|
||||||
cnr.SetSessionToken(stoken)
|
|
||||||
|
|
||||||
if *params.NameScopeGlobal { // we don't check for nil because there is default false value
|
if *params.NameScopeGlobal { // we don't check for nil because there is default false value
|
||||||
if err = checkNNSContainerName(request.ContainerName); err != nil {
|
if err = checkNNSContainerName(request.ContainerName); err != nil {
|
||||||
return nil, fmt.Errorf("invalid container name: %w", err)
|
return cid.ID{}, fmt.Errorf("invalid container name: %w", err)
|
||||||
}
|
}
|
||||||
container.SetNativeName(cnr, request.ContainerName)
|
|
||||||
|
var domain container.Domain
|
||||||
|
domain.SetName(request.ContainerName)
|
||||||
|
container.WriteDomain(&cnr, domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
var prm pool.PrmContainerPut
|
var prm pool.PrmContainerPut
|
||||||
prm.SetContainer(*cnr)
|
prm.SetContainer(cnr)
|
||||||
|
prm.WithinSession(stoken)
|
||||||
|
|
||||||
cnrID, err := p.PutContainer(ctx, prm)
|
cnrID, err := p.PutContainer(ctx, prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("put container: %w", err)
|
return cid.ID{}, fmt.Errorf("put container: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cnrID, nil
|
return *cnrID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkNNSContainerName(name string) error {
|
func checkNNSContainerName(name string) error {
|
||||||
|
@ -383,51 +390,52 @@ func isAlNum(c uint8) bool {
|
||||||
return c >= 'a' && c <= 'z' || c >= '0' && c <= '9'
|
return c >= 'a' && c <= 'z' || c >= '0' && c <= '9'
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareSessionToken(st *SessionToken, isWalletConnect bool) (*session.Token, error) {
|
func prepareSessionToken(st *SessionToken, isWalletConnect bool) (session.Container, error) {
|
||||||
data, err := base64.StdEncoding.DecodeString(st.Token)
|
data, err := base64.StdEncoding.DecodeString(st.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't base64-decode session token: %w", err)
|
return session.Container{}, fmt.Errorf("can't base64-decode session token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := hex.DecodeString(st.Signature)
|
signature, err := hex.DecodeString(st.Signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't decode signature: %w", err)
|
return session.Container{}, fmt.Errorf("couldn't decode signature: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ownerKey, err := keys.NewPublicKeyFromString(st.Key)
|
ownerKey, err := keys.NewPublicKeyFromString(st.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't fetch session token owner key: %w", err)
|
return session.Container{}, fmt.Errorf("couldn't fetch session token owner key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := new(sessionv2.TokenBody)
|
body := new(sessionv2.TokenBody)
|
||||||
if err = body.Unmarshal(data); err != nil {
|
if err = body.Unmarshal(data); err != nil {
|
||||||
return nil, fmt.Errorf("can't unmarshal session token: %w", err)
|
return session.Container{}, fmt.Errorf("can't unmarshal session token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sessionContext, ok := body.GetContext().(*sessionv2.ContainerSessionContext); !ok {
|
if sessionContext, ok := body.GetContext().(*sessionv2.ContainerSessionContext); !ok {
|
||||||
return nil, errors.New("expected container session context but got something different")
|
return session.Container{}, errors.New("expected container session context but got something different")
|
||||||
} else if sessionContext.Verb() != st.Verb {
|
} else if sessionContext.Verb() != st.Verb {
|
||||||
return nil, fmt.Errorf("invalid container session verb '%s', expected: '%s'", sessionContext.Verb().String(), st.Verb.String())
|
return session.Container{}, fmt.Errorf("invalid container session verb '%s', expected: '%s'", sessionContext.Verb().String(), st.Verb.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
stoken := new(session.Token)
|
|
||||||
stoken.ToV2().SetBody(body)
|
|
||||||
|
|
||||||
v2signature := new(refs.Signature)
|
v2signature := new(refs.Signature)
|
||||||
v2signature.SetScheme(refs.ECDSA_SHA512)
|
v2signature.SetScheme(refs.ECDSA_SHA512)
|
||||||
if isWalletConnect {
|
if isWalletConnect {
|
||||||
v2signature.SetScheme(2)
|
v2signature.SetScheme(refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT)
|
||||||
}
|
}
|
||||||
v2signature.SetSign(signature)
|
v2signature.SetSign(signature)
|
||||||
v2signature.SetKey(ownerKey.Bytes())
|
v2signature.SetKey(ownerKey.Bytes())
|
||||||
stoken.ToV2().SetSignature(v2signature)
|
|
||||||
|
|
||||||
if isWalletConnect {
|
var v2token sessionv2.Token
|
||||||
if !walletconnect.Verify((*ecdsa.PublicKey)(ownerKey), []byte(st.Token), signature) {
|
v2token.SetBody(body)
|
||||||
return nil, fmt.Errorf("invalid signature")
|
v2token.SetSignature(v2signature)
|
||||||
}
|
|
||||||
} else if !stoken.VerifySignature() {
|
var stoken session.Container
|
||||||
return nil, fmt.Errorf("invalid signature")
|
if err = stoken.ReadFromV2(v2token); err != nil {
|
||||||
|
return session.Container{}, fmt.Errorf("read from v2 token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stoken.VerifySignature() {
|
||||||
|
return session.Container{}, fmt.Errorf("invalid signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
return stoken, err
|
return stoken, err
|
||||||
|
|
|
@ -2,7 +2,6 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -17,13 +16,11 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
||||||
walletconnect "github.com/nspcc-dev/neofs-rest-gw/internal/wallet-connect"
|
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object/address"
|
|
||||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,7 +36,7 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr
|
||||||
}
|
}
|
||||||
|
|
||||||
var cnrID cid.ID
|
var cnrID cid.ID
|
||||||
if err = cnrID.Parse(*params.Object.ContainerID); err != nil {
|
if err = cnrID.DecodeString(*params.Object.ContainerID); err != nil {
|
||||||
resp := a.logAndGetErrorResponse("invalid container id", err)
|
resp := a.logAndGetErrorResponse("invalid container id", err)
|
||||||
return errorResponse.WithPayload(resp)
|
return errorResponse.WithPayload(resp)
|
||||||
}
|
}
|
||||||
|
@ -60,9 +57,11 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr
|
||||||
return errorResponse.WithPayload(resp)
|
return errorResponse.WithPayload(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
owner := bearer.ResolveIssuer(btoken)
|
||||||
|
|
||||||
obj := object.New()
|
obj := object.New()
|
||||||
obj.SetContainerID(&cnrID)
|
obj.SetContainerID(cnrID)
|
||||||
obj.SetOwnerID(btoken.OwnerID())
|
obj.SetOwnerID(&owner)
|
||||||
obj.SetPayload(payload)
|
obj.SetPayload(payload)
|
||||||
obj.SetAttributes(attributes...)
|
obj.SetAttributes(attributes...)
|
||||||
|
|
||||||
|
@ -101,7 +100,7 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo
|
||||||
}
|
}
|
||||||
|
|
||||||
var prm pool.PrmObjectHead
|
var prm pool.PrmObjectHead
|
||||||
prm.SetAddress(*addr)
|
prm.SetAddress(addr)
|
||||||
prm.UseBearer(btoken)
|
prm.UseBearer(btoken)
|
||||||
|
|
||||||
objInfo, err := a.pool.HeadObject(ctx, prm)
|
objInfo, err := a.pool.HeadObject(ctx, prm)
|
||||||
|
@ -126,7 +125,7 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo
|
||||||
}
|
}
|
||||||
|
|
||||||
var prmRange pool.PrmObjectRange
|
var prmRange pool.PrmObjectRange
|
||||||
prmRange.SetAddress(*addr)
|
prmRange.SetAddress(addr)
|
||||||
prmRange.UseBearer(btoken)
|
prmRange.UseBearer(btoken)
|
||||||
|
|
||||||
var offset, length uint64
|
var offset, length uint64
|
||||||
|
@ -195,7 +194,7 @@ func (a *API) DeleteObject(params operations.DeleteObjectParams, principal *mode
|
||||||
}
|
}
|
||||||
|
|
||||||
var prm pool.PrmObjectDelete
|
var prm pool.PrmObjectDelete
|
||||||
prm.SetAddress(*addr)
|
prm.SetAddress(addr)
|
||||||
prm.UseBearer(btoken)
|
prm.UseBearer(btoken)
|
||||||
|
|
||||||
if err = a.pool.DeleteObject(ctx, prm); err != nil {
|
if err = a.pool.DeleteObject(ctx, prm); err != nil {
|
||||||
|
@ -212,7 +211,7 @@ func (a *API) SearchObjects(params operations.SearchObjectsParams, principal *mo
|
||||||
ctx := params.HTTPRequest.Context()
|
ctx := params.HTTPRequest.Context()
|
||||||
|
|
||||||
var cnrID cid.ID
|
var cnrID cid.ID
|
||||||
if err := cnrID.Parse(params.ContainerID); err != nil {
|
if err := cnrID.DecodeString(params.ContainerID); err != nil {
|
||||||
resp := a.logAndGetErrorResponse("invalid container id", err)
|
resp := a.logAndGetErrorResponse("invalid container id", err)
|
||||||
return errorResponse.WithPayload(resp)
|
return errorResponse.WithPayload(resp)
|
||||||
}
|
}
|
||||||
|
@ -254,7 +253,7 @@ func (a *API) SearchObjects(params operations.SearchObjectsParams, principal *mo
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj, iterateErr = headObjectBaseInfo(ctx, a.pool, &cnrID, &id, btoken); iterateErr != nil {
|
if obj, iterateErr = headObjectBaseInfo(ctx, a.pool, cnrID, id, btoken); iterateErr != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,13 +277,13 @@ func (a *API) SearchObjects(params operations.SearchObjectsParams, principal *mo
|
||||||
return operations.NewSearchObjectsOK().WithPayload(list)
|
return operations.NewSearchObjectsOK().WithPayload(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
func headObjectBaseInfo(ctx context.Context, p *pool.Pool, cnrID *cid.ID, objID *oid.ID, btoken *token.BearerToken) (*models.ObjectBaseInfo, error) {
|
func headObjectBaseInfo(ctx context.Context, p *pool.Pool, cnrID cid.ID, objID oid.ID, btoken bearer.Token) (*models.ObjectBaseInfo, error) {
|
||||||
addr := address.NewAddress()
|
var addr oid.Address
|
||||||
addr.SetContainerID(cnrID)
|
addr.SetContainer(cnrID)
|
||||||
addr.SetObjectID(objID)
|
addr.SetObject(objID)
|
||||||
|
|
||||||
var prm pool.PrmObjectHead
|
var prm pool.PrmObjectHead
|
||||||
prm.SetAddress(*addr)
|
prm.SetAddress(addr)
|
||||||
prm.UseBearer(btoken)
|
prm.UseBearer(btoken)
|
||||||
|
|
||||||
objInfo, err := p.HeadObject(ctx, prm)
|
objInfo, err := p.HeadObject(ctx, prm)
|
||||||
|
@ -309,24 +308,24 @@ func headObjectBaseInfo(ctx context.Context, p *pool.Pool, cnrID *cid.ID, objID
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAddress(containerID, objectID string) (*address.Address, error) {
|
func parseAddress(containerID, objectID string) (oid.Address, error) {
|
||||||
var cnrID cid.ID
|
var cnrID cid.ID
|
||||||
if err := cnrID.Parse(containerID); err != nil {
|
if err := cnrID.DecodeString(containerID); err != nil {
|
||||||
return nil, fmt.Errorf("invalid container id: %w", err)
|
return oid.Address{}, fmt.Errorf("invalid container id: %w", err)
|
||||||
}
|
}
|
||||||
var objID oid.ID
|
var objID oid.ID
|
||||||
if err := objID.Parse(objectID); err != nil {
|
if err := objID.DecodeString(objectID); err != nil {
|
||||||
return nil, fmt.Errorf("invalid object id: %w", err)
|
return oid.Address{}, fmt.Errorf("invalid object id: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := address.NewAddress()
|
var addr oid.Address
|
||||||
addr.SetContainerID(&cnrID)
|
addr.SetContainer(cnrID)
|
||||||
addr.SetObjectID(&objID)
|
addr.SetObject(objID)
|
||||||
|
|
||||||
return addr, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBearerToken(token *models.Principal, signature, key string, isWalletConnect bool) (*token.BearerToken, error) {
|
func getBearerToken(token *models.Principal, signature, key string, isWalletConnect bool) (bearer.Token, error) {
|
||||||
bt := &BearerToken{
|
bt := &BearerToken{
|
||||||
Token: string(*token),
|
Token: string(*token),
|
||||||
Signature: signature,
|
Signature: signature,
|
||||||
|
@ -336,45 +335,46 @@ func getBearerToken(token *models.Principal, signature, key string, isWalletConn
|
||||||
return prepareBearerToken(bt, isWalletConnect)
|
return prepareBearerToken(bt, isWalletConnect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareBearerToken(bt *BearerToken, isWalletConnect bool) (*token.BearerToken, error) {
|
func prepareBearerToken(bt *BearerToken, isWalletConnect bool) (bearer.Token, error) {
|
||||||
data, err := base64.StdEncoding.DecodeString(bt.Token)
|
data, err := base64.StdEncoding.DecodeString(bt.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't base64-decode bearer token: %w", err)
|
return bearer.Token{}, fmt.Errorf("can't base64-decode bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := hex.DecodeString(bt.Signature)
|
signature, err := hex.DecodeString(bt.Signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't decode bearer signature: %w", err)
|
return bearer.Token{}, fmt.Errorf("couldn't decode bearer signature: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ownerKey, err := keys.NewPublicKeyFromString(bt.Key)
|
ownerKey, err := keys.NewPublicKeyFromString(bt.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't fetch bearer token owner key: %w", err)
|
return bearer.Token{}, fmt.Errorf("couldn't fetch bearer token owner key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := new(acl.BearerTokenBody)
|
body := new(acl.BearerTokenBody)
|
||||||
if err = body.Unmarshal(data); err != nil {
|
if err = body.Unmarshal(data); err != nil {
|
||||||
return nil, fmt.Errorf("can't unmarshal bearer token: %w", err)
|
return bearer.Token{}, fmt.Errorf("can't unmarshal bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
btoken := new(token.BearerToken)
|
|
||||||
btoken.ToV2().SetBody(body)
|
|
||||||
|
|
||||||
v2signature := new(refs.Signature)
|
v2signature := new(refs.Signature)
|
||||||
v2signature.SetScheme(refs.ECDSA_SHA512)
|
v2signature.SetScheme(refs.ECDSA_SHA512)
|
||||||
if isWalletConnect {
|
if isWalletConnect {
|
||||||
v2signature.SetScheme(2)
|
v2signature.SetScheme(refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT)
|
||||||
}
|
}
|
||||||
v2signature.SetSign(signature)
|
v2signature.SetSign(signature)
|
||||||
v2signature.SetKey(ownerKey.Bytes())
|
v2signature.SetKey(ownerKey.Bytes())
|
||||||
btoken.ToV2().SetSignature(v2signature)
|
|
||||||
|
|
||||||
if isWalletConnect {
|
var v2btoken acl.BearerToken
|
||||||
if !walletconnect.Verify((*ecdsa.PublicKey)(ownerKey), []byte(bt.Token), signature) {
|
v2btoken.SetBody(body)
|
||||||
return nil, fmt.Errorf("invalid signature")
|
v2btoken.SetSignature(v2signature)
|
||||||
}
|
|
||||||
} else if err = btoken.VerifySignature(); err != nil {
|
var btoken bearer.Token
|
||||||
return nil, fmt.Errorf("invalid signature")
|
if err = btoken.ReadFromV2(v2btoken); err != nil {
|
||||||
|
return bearer.Token{}, fmt.Errorf("read from v2 token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !btoken.VerifySignature() {
|
||||||
|
return bearer.Token{}, fmt.Errorf("invalid signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
return btoken, nil
|
return btoken, nil
|
||||||
|
|
|
@ -2,7 +2,6 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -11,7 +10,6 @@ import (
|
||||||
|
|
||||||
objectv2 "github.com/nspcc-dev/neofs-api-go/v2/object"
|
objectv2 "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
)
|
)
|
||||||
|
@ -134,21 +132,13 @@ func getEpochDurations(ctx context.Context, p *pool.Pool) (*epochDurations, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
res := &epochDurations{
|
res := &epochDurations{
|
||||||
currentEpoch: networkInfo.CurrentEpoch(),
|
currentEpoch: networkInfo.CurrentEpoch(),
|
||||||
msPerBlock: networkInfo.MsPerBlock(),
|
msPerBlock: networkInfo.MsPerBlock(),
|
||||||
|
blockPerEpoch: networkInfo.EpochDuration(),
|
||||||
}
|
}
|
||||||
|
|
||||||
networkInfo.NetworkConfig().IterateParameters(func(parameter *netmap.NetworkParameter) bool {
|
|
||||||
if string(parameter.Key()) == "EpochDuration" {
|
|
||||||
data := make([]byte, 8)
|
|
||||||
copy(data, parameter.Value())
|
|
||||||
res.blockPerEpoch = binary.LittleEndian.Uint64(data)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
if res.blockPerEpoch == 0 {
|
if res.blockPerEpoch == 0 {
|
||||||
return nil, fmt.Errorf("not found param: EpochDuration")
|
return nil, fmt.Errorf("EpochDuration is zero")
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
sessionv2 "github.com/nspcc-dev/neofs-api-go/v2/session"
|
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||||
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToNativeAction converts models.Action to appropriate eacl.Action.
|
// ToNativeAction converts models.Action to appropriate eacl.Action.
|
||||||
|
@ -184,54 +183,40 @@ func FromNativeRole(r eacl.Role) (*models.Role, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToNativeVerb converts models.Verb to appropriate session.ContainerSessionVerb.
|
// ToNativeVerb converts models.Verb to appropriate session.ContainerSessionVerb.
|
||||||
func ToNativeVerb(r *models.Verb) (sessionv2.ContainerSessionVerb, error) {
|
func ToNativeVerb(r *models.Verb) (session.ContainerVerb, error) {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return sessionv2.ContainerVerbUnknown, fmt.Errorf("unsupported empty verb type")
|
return 0, fmt.Errorf("unsupported empty verb type")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *r {
|
switch *r {
|
||||||
case models.VerbPUT:
|
case models.VerbPUT:
|
||||||
return sessionv2.ContainerVerbPut, nil
|
return session.VerbContainerPut, nil
|
||||||
case models.VerbDELETE:
|
case models.VerbDELETE:
|
||||||
return sessionv2.ContainerVerbDelete, nil
|
return session.VerbContainerDelete, nil
|
||||||
case models.VerbSETEACL:
|
case models.VerbSETEACL:
|
||||||
return sessionv2.ContainerVerbSetEACL, nil
|
return session.VerbContainerSetEACL, nil
|
||||||
default:
|
default:
|
||||||
return sessionv2.ContainerVerbUnknown, fmt.Errorf("unsupported verb type: '%s'", *r)
|
return 0, fmt.Errorf("unsupported verb type: '%s'", *r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToNativeRule converts models.Rule to appropriate session.ContainerContext.
|
|
||||||
func ToNativeRule(r *models.Rule) (*session.ContainerContext, error) {
|
|
||||||
var ctx session.ContainerContext
|
|
||||||
|
|
||||||
verb, err := ToNativeVerb(r.Verb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ctx.ToV2().SetVerb(verb)
|
|
||||||
|
|
||||||
if r.ContainerID == "" {
|
|
||||||
ctx.ApplyTo(nil)
|
|
||||||
} else {
|
|
||||||
var cnrID cid.ID
|
|
||||||
if err = cnrID.Parse(r.ContainerID); err != nil {
|
|
||||||
return nil, fmt.Errorf("couldn't parse container id: %w", err)
|
|
||||||
}
|
|
||||||
ctx.ApplyTo(&cnrID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ctx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToNativeContainerToken converts models.Rule to appropriate session.Token.
|
// ToNativeContainerToken converts models.Rule to appropriate session.Token.
|
||||||
func ToNativeContainerToken(tokenRule *models.Rule) (*session.Token, error) {
|
func ToNativeContainerToken(tokenRule *models.Rule) (session.Container, error) {
|
||||||
sctx, err := ToNativeRule(tokenRule)
|
var tok session.Container
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("couldn't transform rule to native: %w", err)
|
if tokenRule.ContainerID != "" {
|
||||||
|
var cnrID cid.ID
|
||||||
|
if err := cnrID.DecodeString(tokenRule.ContainerID); err != nil {
|
||||||
|
return session.Container{}, fmt.Errorf("couldn't parse container id: %w", err)
|
||||||
|
}
|
||||||
|
tok.ApplyOnlyTo(cnrID)
|
||||||
}
|
}
|
||||||
tok := session.NewToken()
|
|
||||||
tok.SetContext(sctx)
|
verb, err := ToNativeVerb(tokenRule.Verb)
|
||||||
|
if err != nil {
|
||||||
|
return session.Container{}, err
|
||||||
|
}
|
||||||
|
tok.ForVerb(verb)
|
||||||
|
|
||||||
return tok, nil
|
return tok, nil
|
||||||
}
|
}
|
||||||
|
@ -368,14 +353,14 @@ func FromNativeTarget(t eacl.Target) (*models.Target, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToNativeObjectToken converts []*models.Record to appropriate token.BearerToken.
|
// ToNativeObjectToken converts []*models.Record to appropriate token.BearerToken.
|
||||||
func ToNativeObjectToken(tokenRecords []*models.Record) (*token.BearerToken, error) {
|
func ToNativeObjectToken(tokenRecords []*models.Record) (*bearer.Token, error) {
|
||||||
table, err := ToNativeTable(tokenRecords)
|
table, err := ToNativeTable(tokenRecords)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var btoken token.BearerToken
|
var btoken bearer.Token
|
||||||
btoken.SetEACLTable(table)
|
btoken.SetEACLTable(*table)
|
||||||
|
|
||||||
return &btoken, nil
|
return &btoken, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
package walletconnect
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
|
|
||||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// saltSize is the salt size added to signed message.
|
|
||||||
saltSize = 16
|
|
||||||
// signatureLen is the length of RFC6979 signature.
|
|
||||||
signatureLen = 64
|
|
||||||
)
|
|
||||||
|
|
||||||
// SignedMessage contains mirrors `SignedMessage` struct from the WalletConnect API.
|
|
||||||
// https://neon.coz.io/wksdk/core/modules.html#SignedMessage
|
|
||||||
type SignedMessage struct {
|
|
||||||
Data []byte
|
|
||||||
Message []byte
|
|
||||||
PublicKey []byte
|
|
||||||
Salt []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign signs message using WalletConnect API. The returned signature
|
|
||||||
// contains RFC6979 signature and 16-byte salt.
|
|
||||||
func Sign(p *ecdsa.PrivateKey, msg []byte) ([]byte, error) {
|
|
||||||
sm, err := SignMessage(p, msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return append(sm.Data, sm.Salt...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify verifies message using WalletConnect API. The returned signature
|
|
||||||
// contains RFC6979 signature and 16-byte salt.
|
|
||||||
func Verify(p *ecdsa.PublicKey, data, sign []byte) bool {
|
|
||||||
if len(sign) != signatureLen+saltSize {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
salt := sign[signatureLen:]
|
|
||||||
return VerifyMessage(p, SignedMessage{
|
|
||||||
Data: sign[:signatureLen],
|
|
||||||
Message: createMessageWithSalt(data, salt),
|
|
||||||
Salt: salt,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignMessage signs message with a private key and returns structure similar to
|
|
||||||
// `signMessage` of the WalletConnect API.
|
|
||||||
// https://github.com/CityOfZion/wallet-connect-sdk/blob/89c236b/packages/wallet-connect-sdk-core/src/index.ts#L496
|
|
||||||
// https://github.com/CityOfZion/neon-wallet/blob/1174a9388480e6bbc4f79eb13183c2a573f67ca8/app/context/WalletConnect/helpers.js#L133
|
|
||||||
func SignMessage(p *ecdsa.PrivateKey, msg []byte) (SignedMessage, error) {
|
|
||||||
var salt [saltSize]byte
|
|
||||||
_, _ = rand.Read(salt[:])
|
|
||||||
|
|
||||||
msg = createMessageWithSalt(msg, salt[:])
|
|
||||||
sign, err := crypto.SignRFC6979(p, msg)
|
|
||||||
if err != nil {
|
|
||||||
return SignedMessage{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignedMessage{
|
|
||||||
Data: sign,
|
|
||||||
Message: msg,
|
|
||||||
PublicKey: elliptic.MarshalCompressed(p.Curve, p.X, p.Y),
|
|
||||||
Salt: salt[:],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyMessage verifies message with a private key and returns structure similar to
|
|
||||||
// `verifyMessage` of WalletConnect API.
|
|
||||||
// https://github.com/CityOfZion/wallet-connect-sdk/blob/89c236b/packages/wallet-connect-sdk-core/src/index.ts#L515
|
|
||||||
// https://github.com/CityOfZion/neon-wallet/blob/1174a9388480e6bbc4f79eb13183c2a573f67ca8/app/context/WalletConnect/helpers.js#L147
|
|
||||||
func VerifyMessage(p *ecdsa.PublicKey, m SignedMessage) bool {
|
|
||||||
if p == nil {
|
|
||||||
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), m.PublicKey)
|
|
||||||
if x == nil || y == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
p = &ecdsa.PublicKey{
|
|
||||||
Curve: elliptic.P256(),
|
|
||||||
X: x,
|
|
||||||
Y: y,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crypto.VerifyRFC6979(p, m.Message, m.Data) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMessageWithSalt(msg, salt []byte) []byte {
|
|
||||||
// 4 byte prefix + length of the message with salt in bytes +
|
|
||||||
// + salt + message + 2 byte postfix.
|
|
||||||
saltedLen := len(salt)*2 + len(msg)
|
|
||||||
data := make([]byte, 4+getVarIntSize(saltedLen)+saltedLen+2)
|
|
||||||
|
|
||||||
n := copy(data, []byte{0x01, 0x00, 0x01, 0xf0}) // fixed prefix
|
|
||||||
n += putVarUint(data[n:], uint64(saltedLen))
|
|
||||||
n += hex.Encode(data[n:], salt[:]) // for some reason we encode salt in hex
|
|
||||||
n += copy(data[n:], msg)
|
|
||||||
copy(data[n:], []byte{0x00, 0x00})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Following functions are copied from github.com/nspcc-dev/neo-go/pkg/io package
|
|
||||||
// to avoid having another dependency.
|
|
||||||
|
|
||||||
// getVarIntSize returns the size in number of bytes of a variable integer.
|
|
||||||
// (reference: GetVarSize(int value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs)
|
|
||||||
func getVarIntSize(value int) int {
|
|
||||||
var size uintptr
|
|
||||||
|
|
||||||
if value < 0xFD {
|
|
||||||
size = 1 // unit8
|
|
||||||
} else if value <= 0xFFFF {
|
|
||||||
size = 3 // byte + uint16
|
|
||||||
} else {
|
|
||||||
size = 5 // byte + uint32
|
|
||||||
}
|
|
||||||
return int(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// putVarUint puts val in varint form to the pre-allocated buffer.
|
|
||||||
func putVarUint(data []byte, val uint64) int {
|
|
||||||
_ = data[8]
|
|
||||||
if val < 0xfd {
|
|
||||||
data[0] = byte(val)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if val < 0xFFFF {
|
|
||||||
data[0] = byte(0xfd)
|
|
||||||
binary.LittleEndian.PutUint16(data[1:], uint16(val))
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
if val < 0xFFFFFFFF {
|
|
||||||
data[0] = byte(0xfe)
|
|
||||||
binary.LittleEndian.PutUint32(data[1:], uint32(val))
|
|
||||||
return 5
|
|
||||||
}
|
|
||||||
|
|
||||||
data[0] = byte(0xff)
|
|
||||||
binary.LittleEndian.PutUint64(data[1:], val)
|
|
||||||
return 9
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
package walletconnect
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
const devenvPrivateKey = "1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb"
|
|
||||||
|
|
||||||
func TestSign(t *testing.T) {
|
|
||||||
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
pubKeyHex := hex.EncodeToString(key.PublicKey().Bytes())
|
|
||||||
|
|
||||||
records := []*models.Record{{
|
|
||||||
Operation: models.NewOperation(models.OperationPUT),
|
|
||||||
Action: models.NewAction(models.ActionALLOW),
|
|
||||||
Filters: []*models.Filter{},
|
|
||||||
Targets: []*models.Target{{
|
|
||||||
Role: models.NewRole(models.RoleOTHERS),
|
|
||||||
Keys: []string{},
|
|
||||||
}},
|
|
||||||
}}
|
|
||||||
|
|
||||||
btoken, err := util.ToNativeObjectToken(records)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
btoken.SetOwner(owner.NewIDFromPublicKey((*ecdsa.PublicKey)(ownerKey)))
|
|
||||||
|
|
||||||
binaryBearer, err := btoken.ToV2().GetBody().StableMarshal(nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
sm, err := SignMessage(&key.PrivateKey, binaryBearer)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
verified := Verify((*ecdsa.PublicKey)(key.PublicKey()), binaryBearer, append(sm.Data, sm.Salt...))
|
|
||||||
require.True(t, verified)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVerifyMessage(t *testing.T) {
|
|
||||||
testCases := [...]struct {
|
|
||||||
publicKey string
|
|
||||||
data string
|
|
||||||
salt string
|
|
||||||
messageHex string
|
|
||||||
messageOriginal string
|
|
||||||
}{
|
|
||||||
{ // Test values from this GIF https://github.com/CityOfZion/neon-wallet/pull/2390 .
|
|
||||||
publicKey: "02ce6228ba2cb2fc235be93aff9cd5fc0851702eb9791552f60db062f01e3d83f6",
|
|
||||||
data: "90ab1886ca0bece59b982d9ade8f5598065d651362fb9ce45ad66d0474b89c0b80913c8f0118a282acbdf200a429ba2d81bc52534a53ab41a2c6dfe2f0b4fb1b",
|
|
||||||
salt: "d41e348afccc2f3ee45cd9f5128b16dc",
|
|
||||||
messageHex: "010001f05c6434316533343861666363633266336565343563643966353132386231366463436172616c686f2c206d756c65712c206f2062616775697520656820697373756d65726d6f2074616978206c696761646f206e61206d697373e36f3f0000",
|
|
||||||
messageOriginal: "436172616c686f2c206d756c65712c206f2062616775697520656820697373756d65726d6f2074616978206c696761646f206e61206d697373e36f3f",
|
|
||||||
},
|
|
||||||
{ // Test value from wallet connect integration test
|
|
||||||
publicKey: "03bd9108c0b49f657e9eee50d1399022bd1e436118e5b7529a1b7cd606652f578f",
|
|
||||||
data: "510caa8cb6db5dedf04d215a064208d64be7496916d890df59aee132db8f2b07532e06f7ea664c4a99e3bcb74b43a35eb9653891b5f8701d2aef9e7526703eaa",
|
|
||||||
salt: "2c5b189569e92cce12e1c640f23e83ba",
|
|
||||||
messageHex: "010001f02632633562313839353639653932636365313265316336343066323365383362613132333435360000",
|
|
||||||
messageOriginal: "313233343536", // ascii string "123456"
|
|
||||||
},
|
|
||||||
{ // Test value from wallet connect integration test
|
|
||||||
publicKey: "03bd9108c0b49f657e9eee50d1399022bd1e436118e5b7529a1b7cd606652f578f",
|
|
||||||
data: "1e13f248962d8b3b60708b55ddf448d6d6a28c6b43887212a38b00bf6bab695e61261e54451c6e3d5f1f000e5534d166c7ca30f662a296d3a9aafa6d8c173c01",
|
|
||||||
salt: "58c86b2e74215b4f36b47d731236be3b",
|
|
||||||
messageHex: "010001f02035386338366232653734323135623466333662343764373331323336626533620000",
|
|
||||||
messageOriginal: "", // empty string
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
|
||||||
pub, err := keys.NewPublicKeyFromString(testCase.publicKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
data, err := hex.DecodeString(testCase.data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
salt, err := hex.DecodeString(testCase.salt)
|
|
||||||
require.NoError(t, err)
|
|
||||||
msg, err := hex.DecodeString(testCase.messageHex)
|
|
||||||
require.NoError(t, err)
|
|
||||||
orig, err := hex.DecodeString(testCase.messageOriginal)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, msg, createMessageWithSalt(orig, salt))
|
|
||||||
|
|
||||||
sm := SignedMessage{
|
|
||||||
Data: data,
|
|
||||||
Message: msg,
|
|
||||||
PublicKey: pub.Bytes(),
|
|
||||||
Salt: salt,
|
|
||||||
}
|
|
||||||
require.True(t, VerifyMessage(nil, sm))
|
|
||||||
|
|
||||||
require.True(t, Verify((*ecdsa.PublicKey)(pub), orig, append(data, salt...)))
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue