[#16] Update SDK to v1.0.0-rc.5

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-07-12 11:01:21 +03:00 committed by Kirillov Denis
parent 0df815ed27
commit aeb68fdd7a
12 changed files with 313 additions and 559 deletions

View file

@ -25,16 +25,16 @@ import (
"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/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/acl"
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/netmap"
"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"
"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/user"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
@ -59,7 +59,7 @@ const (
XBearerOwnerID = "X-Bearer-Owner-Id"
// tests configuration.
useWalletConnect = false
useWalletConnect = true
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) {
aioImage := "nspccdev/neofs-aio-testcontainer:"
versions := []string{
//"0.24.0",
//"0.25.1",
//"0.26.1",
//"0.27.5",
"0.27.7",
"0.29.0",
//"latest",
}
@ -112,25 +108,28 @@ func runTests(ctx context.Context, t *testing.T, key *keys.PrivateKey, version s
cancel := runServer(ctx, t, node)
defer cancel()
var owner user.ID
user.IDFromKey(&owner, key.PrivateKey.PublicKey)
clientPool := getPool(ctx, t, key, node)
cnrID := createContainer(ctx, t, clientPool, containerName)
cnrID := createContainer(ctx, t, clientPool, owner, containerName)
restrictByEACL(ctx, t, clientPool, cnrID)
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 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 delete object "+version, func(t *testing.T) { restObjectDelete(ctx, t, clientPool, cnrID) })
t.Run("rest search objects "+version, func(t *testing.T) { restObjectsSearch(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, &owner, 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 "+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 delete container "+version, func(t *testing.T) { restContainerDelete(ctx, t, clientPool) })
t.Run("rest put container eacl "+version, func(t *testing.T) { restContainerEACLPut(ctx, t, clientPool) })
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, owner) })
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 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 {
@ -270,7 +269,7 @@ func authTokens(ctx context.Context, t *testing.T) {
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{
{
Name: "all-object",
@ -332,8 +331,8 @@ func checkPutContainerWithError(t *testing.T, httpClient *http.Client, token *ha
checkGWErrorResponse(t, httpClient, request)
}
func checkDeleteContainerWithError(t *testing.T, httpClient *http.Client, cnrID *cid.ID, token *handlers.BearerToken) {
reqURL, err := url.Parse(testHost + "/v1/containers/" + cnrID.String())
func checkDeleteContainerWithError(t *testing.T, httpClient *http.Client, cnrID cid.ID, token *handlers.BearerToken) {
reqURL, err := url.Parse(testHost + "/v1/containers/" + cnrID.EncodeToString())
require.NoError(t, err)
request, err := http.NewRequest(http.MethodDelete, reqURL.String(), nil)
require.NoError(t, err)
@ -342,20 +341,20 @@ func checkDeleteContainerWithError(t *testing.T, httpClient *http.Client, cnrID
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{}}
body, err := json.Marshal(&req)
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)
prepareCommonHeaders(request.Header, token)
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{
ContainerID: util.NewString(cnrID.String()),
ContainerID: util.NewString(cnrID.EncodeToString()),
FileName: util.NewString("newFile.txt"),
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)
}
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{
Object: []*models.Record{{
Operation: models.NewOperation(models.OperationPUT),
@ -404,7 +403,7 @@ func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnr
}
req := &models.ObjectUpload{
ContainerID: util.NewString(cnrID.String()),
ContainerID: util.NewString(cnrID.EncodeToString()),
FileName: util.NewString("newFile.txt"),
Payload: base64.StdEncoding.EncodeToString([]byte(content)),
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)
var CID cid.ID
err = CID.Parse(*addr.ContainerID)
err = CID.DecodeString(*addr.ContainerID)
require.NoError(t, err)
var id oid.ID
err = id.Parse(*addr.ObjectID)
err = id.DecodeString(*addr.ObjectID)
require.NoError(t, err)
objectAddress := address.NewAddress()
objectAddress.SetContainerID(&CID)
objectAddress.SetObjectID(&id)
var objectAddress oid.Address
objectAddress.SetContainer(CID)
objectAddress.SetObject(id)
var prm pool.PrmObjectGet
prm.SetAddress(*objectAddress)
prm.SetAddress(objectAddress)
res, err := clientPool.GetObject(ctx, prm)
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")
attributes := map[string]string{
object.AttributeFileName: "get-obj-name",
"user-attribute": "user value",
}
objID := createObject(ctx, t, p, cnrID, attributes, content)
objID := createObject(ctx, t, p, ownerID, cnrID, attributes, content)
bearer := &models.Bearer{
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.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)
prepareCommonHeaders(request.Header, bearerToken)
objInfo := &models.ObjectInfo{}
doRequest(t, httpClient, request, http.StatusOK, objInfo)
require.Equal(t, cnrID.String(), *objInfo.ContainerID)
require.Equal(t, objID.String(), *objInfo.ObjectID)
require.Equal(t, p.OwnerID().String(), *objInfo.OwnerID)
require.Equal(t, cnrID.EncodeToString(), *objInfo.ContainerID)
require.Equal(t, objID.EncodeToString(), *objInfo.ObjectID)
require.Equal(t, ownerID.EncodeToString(), *objInfo.OwnerID)
require.Equal(t, len(attributes), len(objInfo.Attributes))
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("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)
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-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)
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)
}
func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.ID) {
objID := createObject(ctx, t, p, cnrID, nil, []byte("some content"))
func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, owner *user.ID, cnrID cid.ID) {
objID := createObject(ctx, t, p, owner, cnrID, nil, []byte("some content"))
bearer := &models.Bearer{
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.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)
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)
require.True(t, *resp.Success)
var addr address.Address
addr.SetContainerID(cnrID)
addr.SetObjectID(objID)
var addr oid.Address
addr.SetContainer(cnrID)
addr.SetObject(objID)
var prm pool.PrmObjectHead
prm.SetAddress(addr)
@ -588,16 +587,16 @@ func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *ci
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"
objectName := "object-name"
headers := map[string]string{
object.AttributeFileName: objectName,
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"
_ = createObject(ctx, t, p, cnrID, headers, []byte("some content"))
_ = createObject(ctx, t, p, owner, cnrID, headers, []byte("some content"))
bearer := &models.Bearer{
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.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)
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)
objBaseInfo := resp.Objects[0]
require.Equal(t, cnrID.String(), *objBaseInfo.Address.ContainerID)
require.Equal(t, objID.String(), *objBaseInfo.Address.ObjectID)
require.Equal(t, cnrID.EncodeToString(), *objBaseInfo.Address.ContainerID)
require.Equal(t, objID.EncodeToString(), *objBaseInfo.Address.ObjectID)
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)
}
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}
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)
request = request.WithContext(ctx)
cnrInfo := &models.ContainerInfo{}
doRequest(t, httpClient, request, http.StatusOK, cnrInfo)
require.Equal(t, cnrID.String(), *cnrInfo.ContainerID)
require.Equal(t, clientPool.OwnerID().String(), *cnrInfo.OwnerID)
require.Equal(t, cnrID.EncodeToString(), *cnrInfo.ContainerID)
require.Equal(t, owner.EncodeToString(), *cnrInfo.OwnerID)
}
func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Pool) {
cnrID := createContainer(ctx, t, clientPool, "for-delete")
func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Pool, owner user.ID) {
cnrID := createContainer(ctx, t, clientPool, owner, "for-delete")
bearer := &models.Bearer{
Container: &models.Rule{
@ -711,7 +710,7 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
query := make(url.Values)
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)
request = request.WithContext(ctx)
prepareCommonHeaders(request.Header, bearerToken)
@ -721,15 +720,15 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
require.True(t, *resp.Success)
var prm pool.PrmContainerGet
prm.SetContainerID(*cnrID)
prm.SetContainerID(cnrID)
_, err = clientPool.GetContainer(ctx, prm)
require.Error(t, err)
require.Contains(t, err.Error(), "not found")
}
func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Pool) {
cnrID := createContainer(ctx, t, clientPool, "for-eacl-put")
func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, owner user.ID) {
cnrID := createContainer(ctx, t, clientPool, owner, "for-eacl-put")
httpClient := &http.Client{Timeout: 60 * time.Second}
bearer := &models.Bearer{
Container: &models.Rule{
@ -757,7 +756,7 @@ func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Po
query := make(url.Values)
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)
request = request.WithContext(ctx)
prepareCommonHeaders(request.Header, bearerToken)
@ -767,7 +766,7 @@ func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Po
require.True(t, *resp.Success)
var prm pool.PrmContainerEACL
prm.SetContainerID(*cnrID)
prm.SetContainerID(cnrID)
table, err := clientPool.GetEACL(ctx, prm)
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))
}
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
prm.SetContainerID(*cnrID)
prm.SetContainerID(cnrID)
expectedTable, err := p.GetEACL(ctx, prm)
require.NoError(t, err)
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)
request = request.WithContext(ctx)
responseTable := &models.Eacl{}
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)
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))
}
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
prm.SetOwnerID(*p.OwnerID())
prm.SetOwnerID(owner)
ids, err := p.ListContainers(ctx, prm)
require.NoError(t, err)
@ -813,7 +812,7 @@ func restContainerList(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *c
httpClient := defaultHTTPClient()
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)
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.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 {
for _, cnrInfo := range containers {
if *cnrInfo.ContainerID == cnrID {
for _, attr := range cnrInfo.Attributes {
if *attr.Key == container.AttributeName && *attr.Value == cnrName {
if *attr.Key == "Name" && *attr.Value == cnrName {
return true
}
}
@ -848,7 +847,8 @@ func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearers []*models.B
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
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)
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 {
b64Token := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
base64.StdEncoding.Encode(b64Token, data)
sm, err := walletconnect.SignMessage(&key.PrivateKey, b64Token[:])
signer := neofsecdsa.SignerWalletConnect(key.PrivateKey)
signature, err := signer.Sign(data)
require.NoError(t, err)
return &handlers.BearerToken{
Token: string(b64Token),
Signature: hex.EncodeToString(append(sm.Data, sm.Salt...)),
Token: base64.StdEncoding.EncodeToString(data),
Signature: hex.EncodeToString(signature),
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)
var CID cid.ID
err = CID.Parse(*addr.ContainerID)
err = CID.DecodeString(*addr.ContainerID)
require.NoError(t, err)
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)
require.NoError(t, err)
cnrAttr := make(map[string]string, len(cnr.Attributes()))
for _, attribute := range cnr.Attributes() {
cnrAttr[attribute.Key()] = attribute.Value()
}
cnrAttr := make(map[string]string)
cnr.IterateAttributes(func(key, val string) {
cnrAttr[key] = val
})
for key, val := range userAttributes {
require.Equal(t, val, cnrAttr[key])
@ -1045,32 +1044,38 @@ func prepareCommonHeaders(header http.Header, bearerToken *handlers.BearerToken)
header.Add(XBearerSignatureKey, bearerToken.Key)
}
func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, name string) *cid.ID {
pp, err := policy.Parse("REP 1")
func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, owner user.ID, name string) cid.ID {
var policy netmap.PlacementPolicy
err := policy.DecodeString("REP 1")
require.NoError(t, err)
cnr := container.New(
container.WithPolicy(pp),
container.WithCustomBasicACL(0x0FFFFFFF),
container.WithAttribute(container.AttributeName, name),
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)))
cnr.SetOwnerID(clientPool.OwnerID())
var cnr container.Container
cnr.Init()
cnr.SetOwner(owner)
cnr.SetPlacementPolicy(policy)
cnr.SetBasicACL(acl.PublicRWExtended)
container.SetName(&cnr, name)
container.SetCreationTime(&cnr, time.Now())
err = pool.SyncContainerWithNetwork(ctx, &cnr, clientPool)
require.NoError(t, err)
var waitPrm pool.WaitParams
waitPrm.SetPollInterval(3 * time.Second)
waitPrm.SetTimeout(15 * time.Second)
var prm pool.PrmContainerPut
prm.SetContainer(*cnr)
prm.SetContainer(cnr)
prm.SetWaitParams(waitPrm)
CID, err := clientPool.PutContainer(ctx, prm)
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))
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.SetOwnerID(p.OwnerID())
obj.SetOwnerID(ownerID)
obj.SetContainerID(cnrID)
obj.SetAttributes(attributes...)
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)
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.SetCID(cnrID)

7
go.mod
View file

@ -12,8 +12,8 @@ require (
github.com/go-openapi/validate v0.21.0
github.com/google/uuid v1.3.0
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-sdk-go v1.0.0-rc.3.0.20220412151250-3e75660802ae
github.com/nspcc-dev/neofs-api-go/v2 v2.13.0
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.5
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.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/mr-tron/base58 v1.2.0 // 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/tzhash v1.5.2 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect

12
go.sum
View file

@ -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/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.12.1 h1:PVU2rLlG9S0jDe5eKyaUs4nKo/la+mN5pvz32Gib3qM=
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 h1:7BcBiSjmWqJx0SPFlGRYt9ZFbMjucRIz9+Mv8UBLeq8=
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.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/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-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.3.0.20220412151250-3e75660802ae/go.mod h1:Hl7a1l0ntZ4b1ZABpGX6fuAuFS3c6+hyMCUNVvZv/w4=
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.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.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/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/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
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-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-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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=

View file

@ -14,6 +14,7 @@ import (
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
"github.com/nspcc-dev/neofs-sdk-go/pool"
"github.com/nspcc-dev/neofs-sdk-go/user"
"go.uber.org/zap"
)
@ -22,6 +23,7 @@ type API struct {
log *zap.Logger
pool *pool.Pool
key *keys.PrivateKey
owner *user.ID
defaultTimestamp bool
}
@ -59,10 +61,14 @@ const (
// New creates a new API using specified logger, connection pool and other parameters.
func New(prm *PrmAPI) *API {
var owner user.ID
user.IDFromKey(&owner, prm.Key.PrivateKey.PublicKey)
return &API{
log: prm.Logger,
pool: prm.Pool,
key: prm.Key,
owner: &owner,
defaultTimestamp: prm.DefaultTimestamp,
}
}

View file

@ -8,11 +8,15 @@ import (
"github.com/go-openapi/runtime/middleware"
"github.com/google/uuid"
"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/restapi/operations"
"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/user"
)
const defaultTokenExpDuration = 100 // in epoch
@ -83,7 +87,7 @@ func (a *API) PostAuth(params operations.AuthParams) middleware.Responder {
if isObject {
prm := newObjectParams(commonPrm, token)
response[i], err = prepareObjectToken(ctx, prm, a.pool)
response[i], err = prepareObjectToken(ctx, prm, a.pool, *a.owner)
} else {
prm := newContainerParams(commonPrm, token)
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)
}
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)
if err != nil {
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)
if err != nil {
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)
if err != nil {
return nil, fmt.Errorf("couldn't marshal bearer token: %w", err)
}
var v2token acl.BearerToken
btoken.WriteToV2(&v2token)
binaryBearer := v2token.GetBody().StableMarshal(nil)
return &models.TokenResponse{
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)
}
var ownerID owner.ID
if err = ownerID.Parse(params.XBearerOwnerID); err != nil {
var ownerID user.ID
if err = ownerID.DecodeString(params.XBearerOwnerID); err != nil {
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)
}
uid, err := uuid.New().MarshalBinary()
if err != nil {
return nil, err
}
stoken.SetID(uid)
stoken.SetOwnerID(&ownerID)
stoken.SetID(uuid.New())
stoken.SetIat(iat)
stoken.SetExp(exp)
stoken.SetSessionKey(key.Bytes())
binaryToken, err := stoken.ToV2().GetBody().StableMarshal(nil)
if err != nil {
return nil, fmt.Errorf("couldn't marshal session token: %w", err)
}
authKey := neofsecdsa.PublicKey(*key)
stoken.SetAuthKey(&authKey)
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{
Name: params.Name,

View file

@ -7,12 +7,14 @@ import (
"crypto/sha512"
"encoding/base64"
"encoding/hex"
"math"
"testing"
"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/internal/util"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/stretchr/testify/require"
)
@ -37,14 +39,19 @@ func TestSign(t *testing.T) {
btoken, err := util.ToNativeObjectToken(records)
require.NoError(t, err)
btoken.SetExp(math.MaxInt64)
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)
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)
require.NoError(t, err)
var v2token acl.BearerToken
btoken.WriteToV2(&v2token)
binaryBearer := v2token.GetBody().StableMarshal(nil)
bearerBase64 := base64.StdEncoding.EncodeToString(binaryBearer)
h := sha512.Sum512(binaryBearer)

View file

@ -2,13 +2,11 @@ package handlers
import (
"context"
"crypto/ecdsa"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
@ -19,20 +17,21 @@ import (
"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/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/acl"
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/policy"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/pool"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
"go.uber.org/zap"
)
const (
defaultPlacementPolicy = "REP 3"
defaultBasicACL = acl.PrivateBasicName
defaultBasicACL = acl.NamePrivate
attributeName = "Name"
attributeTimestamp = "Timestamp"
)
// PutContainers handler that creates container in NeoFS.
@ -60,7 +59,7 @@ func (a *API) PutContainers(params operations.PutContainerParams, principal *mod
}
var resp operations.PutContainerOKBody
resp.ContainerID = util.NewString(cnrID.String())
resp.ContainerID = util.NewString(cnrID.EncodeToString())
return operations.NewPutContainerOK().WithPayload(&resp)
}
@ -73,7 +72,7 @@ func (a *API) GetContainer(params operations.GetContainerParams) middleware.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 {
resp := a.logAndGetErrorResponse("get container", err)
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 {
ctx := params.HTTPRequest.Context()
var ownerID owner.ID
if err := ownerID.Parse(params.OwnerID); err != nil {
var ownerID user.ID
if err := ownerID.DecodeString(params.OwnerID); err != nil {
resp := a.logAndGetErrorResponse("invalid owner id", err)
return operations.NewListContainersBadRequest().WithPayload(resp)
}
@ -203,8 +202,8 @@ func (a *API) DeleteContainer(params operations.DeleteContainerParams, principal
}
var prm pool.PrmContainerDelete
prm.SetContainerID(*cnrID)
prm.SetSessionToken(*stoken)
prm.SetContainerID(cnrID)
prm.SetSessionToken(stoken)
if err = a.pool.DeleteContainer(params.HTTPRequest.Context(), prm); err != nil {
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
}
attrs := make([]*models.Attribute, len(cnr.Attributes()))
for i, attr := range cnr.Attributes() {
attrs[i] = &models.Attribute{
Key: util.NewString(attr.Key()),
Value: util.NewString(attr.Value()),
}
var attrs []*models.Attribute
cnr.IterateAttributes(func(key, val string) {
attrs = append(attrs, &models.Attribute{
Key: util.NewString(key),
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{
ContainerID: util.NewString(cnrID.String()),
Version: util.NewString(cnr.Version().String()),
OwnerID: util.NewString(cnr.OwnerID().String()),
BasicACL: util.NewString(acl.BasicACL(cnr.BasicACL()).String()),
PlacementPolicy: util.NewString(strings.Join(policy.Encode(cnr.PlacementPolicy()), " ")),
OwnerID: util.NewString(cnr.Owner().String()),
BasicACL: util.NewString(cnr.BasicACL().EncodeToString()),
PlacementPolicy: util.NewString(sb.String()),
Attributes: attrs,
}, nil
}
func prepareUserAttributes(header http.Header) map[string]string {
filtered := filterHeaders(header)
delete(filtered, container.AttributeName)
delete(filtered, container.AttributeTimestamp)
delete(filtered, attributeName)
delete(filtered, attributeTimestamp)
return filtered
}
func parseContainerID(containerID string) (*cid.ID, error) {
func parseContainerID(containerID string) (cid.ID, error) {
var cnrID cid.ID
if err := cnrID.Parse(containerID); err != nil {
return nil, fmt.Errorf("parse container id '%s': %w", containerID, err)
if err := cnrID.DecodeString(containerID); err != nil {
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)
if err != nil {
return err
}
table.SetCID(cnrID)
table.SetSessionToken(stoken)
var prm pool.PrmContainerSetEACL
prm.SetTable(*table)
prm.WithinSession(stoken)
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
prm.SetContainerID(*cnrID)
prm.SetContainerID(cnrID)
table, err := p.GetEACL(ctx, prm)
if err != nil {
@ -282,7 +285,7 @@ func getContainerEACL(ctx context.Context, p *pool.Pool, cnrID *cid.ID) (*models
}
tableResp := &models.Eacl{
ContainerID: cnrID.String(),
ContainerID: cnrID.EncodeToString(),
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
}
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
if request.PlacementPolicy == "" {
request.PlacementPolicy = defaultPlacementPolicy
}
pp, err := policy.Parse(request.PlacementPolicy)
var policy netmap.PlacementPolicy
err := policy.DecodeString(request.PlacementPolicy)
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 == "" {
request.BasicACL = defaultBasicACL
}
basicACL, err := acl.ParseBasicACL(request.BasicACL)
if err != nil {
return nil, fmt.Errorf("couldn't parse basic acl: %w", err)
var basicACL acl.Basic
if err = basicACL.DecodeString(request.BasicACL); err != nil {
return cid.ID{}, fmt.Errorf("couldn't parse basic acl: %w", err)
}
cnrOptions := []container.Option{
container.WithPolicy(pp),
container.WithCustomBasicACL(basicACL),
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)),
}
var cnr container.Container
cnr.Init()
cnr.SetPlacementPolicy(policy)
cnr.SetBasicACL(basicACL)
cnr.SetOwner(stoken.Issuer())
container.SetCreationTime(&cnr, time.Now())
if request.ContainerName != "" {
container.WithAttribute(container.AttributeName, request.ContainerName)
container.SetName(&cnr, request.ContainerName)
}
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 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
prm.SetContainer(*cnr)
prm.SetContainer(cnr)
prm.WithinSession(stoken)
cnrID, err := p.PutContainer(ctx, prm)
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 {
@ -383,51 +390,52 @@ func isAlNum(c uint8) bool {
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)
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)
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)
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)
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 {
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 {
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.SetScheme(refs.ECDSA_SHA512)
if isWalletConnect {
v2signature.SetScheme(2)
v2signature.SetScheme(refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT)
}
v2signature.SetSign(signature)
v2signature.SetKey(ownerKey.Bytes())
stoken.ToV2().SetSignature(v2signature)
if isWalletConnect {
if !walletconnect.Verify((*ecdsa.PublicKey)(ownerKey), []byte(st.Token), signature) {
return nil, fmt.Errorf("invalid signature")
}
} else if !stoken.VerifySignature() {
return nil, fmt.Errorf("invalid signature")
var v2token sessionv2.Token
v2token.SetBody(body)
v2token.SetSignature(v2signature)
var stoken session.Container
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

View file

@ -2,7 +2,6 @@ package handlers
import (
"context"
"crypto/ecdsa"
"encoding/base64"
"encoding/hex"
"errors"
@ -17,13 +16,11 @@ import (
"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/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"
"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"
"github.com/nspcc-dev/neofs-sdk-go/pool"
"github.com/nspcc-dev/neofs-sdk-go/token"
"go.uber.org/zap"
)
@ -39,7 +36,7 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr
}
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)
return errorResponse.WithPayload(resp)
}
@ -60,9 +57,11 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr
return errorResponse.WithPayload(resp)
}
owner := bearer.ResolveIssuer(btoken)
obj := object.New()
obj.SetContainerID(&cnrID)
obj.SetOwnerID(btoken.OwnerID())
obj.SetContainerID(cnrID)
obj.SetOwnerID(&owner)
obj.SetPayload(payload)
obj.SetAttributes(attributes...)
@ -101,7 +100,7 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo
}
var prm pool.PrmObjectHead
prm.SetAddress(*addr)
prm.SetAddress(addr)
prm.UseBearer(btoken)
objInfo, err := a.pool.HeadObject(ctx, prm)
@ -126,7 +125,7 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo
}
var prmRange pool.PrmObjectRange
prmRange.SetAddress(*addr)
prmRange.SetAddress(addr)
prmRange.UseBearer(btoken)
var offset, length uint64
@ -195,7 +194,7 @@ func (a *API) DeleteObject(params operations.DeleteObjectParams, principal *mode
}
var prm pool.PrmObjectDelete
prm.SetAddress(*addr)
prm.SetAddress(addr)
prm.UseBearer(btoken)
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()
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)
return errorResponse.WithPayload(resp)
}
@ -254,7 +253,7 @@ func (a *API) SearchObjects(params operations.SearchObjectsParams, principal *mo
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
}
@ -278,13 +277,13 @@ func (a *API) SearchObjects(params operations.SearchObjectsParams, principal *mo
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) {
addr := address.NewAddress()
addr.SetContainerID(cnrID)
addr.SetObjectID(objID)
func headObjectBaseInfo(ctx context.Context, p *pool.Pool, cnrID cid.ID, objID oid.ID, btoken bearer.Token) (*models.ObjectBaseInfo, error) {
var addr oid.Address
addr.SetContainer(cnrID)
addr.SetObject(objID)
var prm pool.PrmObjectHead
prm.SetAddress(*addr)
prm.SetAddress(addr)
prm.UseBearer(btoken)
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
}
func parseAddress(containerID, objectID string) (*address.Address, error) {
func parseAddress(containerID, objectID string) (oid.Address, error) {
var cnrID cid.ID
if err := cnrID.Parse(containerID); err != nil {
return nil, fmt.Errorf("invalid container id: %w", err)
if err := cnrID.DecodeString(containerID); err != nil {
return oid.Address{}, fmt.Errorf("invalid container id: %w", err)
}
var objID oid.ID
if err := objID.Parse(objectID); err != nil {
return nil, fmt.Errorf("invalid object id: %w", err)
if err := objID.DecodeString(objectID); err != nil {
return oid.Address{}, fmt.Errorf("invalid object id: %w", err)
}
addr := address.NewAddress()
addr.SetContainerID(&cnrID)
addr.SetObjectID(&objID)
var addr oid.Address
addr.SetContainer(cnrID)
addr.SetObject(objID)
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{
Token: string(*token),
Signature: signature,
@ -336,45 +335,46 @@ func getBearerToken(token *models.Principal, signature, key string, isWalletConn
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)
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)
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)
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)
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.SetScheme(refs.ECDSA_SHA512)
if isWalletConnect {
v2signature.SetScheme(2)
v2signature.SetScheme(refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT)
}
v2signature.SetSign(signature)
v2signature.SetKey(ownerKey.Bytes())
btoken.ToV2().SetSignature(v2signature)
if isWalletConnect {
if !walletconnect.Verify((*ecdsa.PublicKey)(ownerKey), []byte(bt.Token), signature) {
return nil, fmt.Errorf("invalid signature")
}
} else if err = btoken.VerifySignature(); err != nil {
return nil, fmt.Errorf("invalid signature")
var v2btoken acl.BearerToken
v2btoken.SetBody(body)
v2btoken.SetSignature(v2signature)
var btoken bearer.Token
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

View file

@ -2,7 +2,6 @@ package handlers
import (
"context"
"encoding/binary"
"fmt"
"net/http"
"strconv"
@ -11,7 +10,6 @@ import (
objectv2 "github.com/nspcc-dev/neofs-api-go/v2/object"
"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/pool"
)
@ -134,21 +132,13 @@ func getEpochDurations(ctx context.Context, p *pool.Pool) (*epochDurations, erro
}
res := &epochDurations{
currentEpoch: networkInfo.CurrentEpoch(),
msPerBlock: networkInfo.MsPerBlock(),
currentEpoch: networkInfo.CurrentEpoch(),
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 {
return nil, fmt.Errorf("not found param: EpochDuration")
return nil, fmt.Errorf("EpochDuration is zero")
}
return res, nil
}

View file

@ -5,14 +5,13 @@ import (
"errors"
"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-sdk-go/bearer"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
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/object"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/token"
)
// 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.
func ToNativeVerb(r *models.Verb) (sessionv2.ContainerSessionVerb, error) {
func ToNativeVerb(r *models.Verb) (session.ContainerVerb, error) {
if r == nil {
return sessionv2.ContainerVerbUnknown, fmt.Errorf("unsupported empty verb type")
return 0, fmt.Errorf("unsupported empty verb type")
}
switch *r {
case models.VerbPUT:
return sessionv2.ContainerVerbPut, nil
return session.VerbContainerPut, nil
case models.VerbDELETE:
return sessionv2.ContainerVerbDelete, nil
return session.VerbContainerDelete, nil
case models.VerbSETEACL:
return sessionv2.ContainerVerbSetEACL, nil
return session.VerbContainerSetEACL, nil
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.
func ToNativeContainerToken(tokenRule *models.Rule) (*session.Token, error) {
sctx, err := ToNativeRule(tokenRule)
if err != nil {
return nil, fmt.Errorf("couldn't transform rule to native: %w", err)
func ToNativeContainerToken(tokenRule *models.Rule) (session.Container, error) {
var tok session.Container
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
}
@ -368,14 +353,14 @@ func FromNativeTarget(t eacl.Target) (*models.Target, error) {
}
// 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)
if err != nil {
return nil, err
}
var btoken token.BearerToken
btoken.SetEACLTable(table)
var btoken bearer.Token
btoken.SetEACLTable(*table)
return &btoken, nil
}

View file

@ -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
}

View file

@ -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...)))
}
}