forked from TrueCloudLab/frostfs-rest-gw
[#1] Add wallet connect support
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
f5eab95f95
commit
c7c570fd10
14 changed files with 715 additions and 114 deletions
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi"
|
||||||
"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"
|
||||||
|
walletconnect "github.com/nspcc-dev/neofs-rest-gw/wallet-connect"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/container"
|
"github.com/nspcc-dev/neofs-sdk-go/container"
|
||||||
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"
|
||||||
|
@ -42,9 +43,12 @@ const (
|
||||||
devenvPrivateKey = "1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb"
|
devenvPrivateKey = "1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb"
|
||||||
testListenAddress = "localhost:8082"
|
testListenAddress = "localhost:8082"
|
||||||
testHost = "http://" + testListenAddress
|
testHost = "http://" + testListenAddress
|
||||||
testNode = "localhost:8080"
|
testContainerNode = "localhost:8080"
|
||||||
|
testLocalNode = "s01.neofs.devenv:8080"
|
||||||
containerName = "test-container"
|
containerName = "test-container"
|
||||||
|
localVersion = "local"
|
||||||
|
|
||||||
|
walletConnectQuery = "walletConnect"
|
||||||
// XBearerSignature header contains base64 encoded signature of the token body.
|
// XBearerSignature header contains base64 encoded signature of the token body.
|
||||||
XBearerSignature = "X-Bearer-Signature"
|
XBearerSignature = "X-Bearer-Signature"
|
||||||
// XBearerSignatureKey header contains hex encoded public key that corresponds the signature of the token body.
|
// XBearerSignatureKey header contains hex encoded public key that corresponds the signature of the token body.
|
||||||
|
@ -52,10 +56,29 @@ const (
|
||||||
// XBearerScope header contains operation scope for auth (bearer) token.
|
// XBearerScope header contains operation scope for auth (bearer) token.
|
||||||
// It corresponds to 'object' or 'container' services in neofs.
|
// It corresponds to 'object' or 'container' services in neofs.
|
||||||
XBearerScope = "X-Bearer-Scope"
|
XBearerScope = "X-Bearer-Scope"
|
||||||
|
|
||||||
|
// configuration tests
|
||||||
|
useWalletConnect = false
|
||||||
|
useLocalEnvironment = false
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIntegration(t *testing.T) {
|
func TestIntegration(t *testing.T) {
|
||||||
rootCtx := context.Background()
|
ctx := context.Background()
|
||||||
|
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if useLocalEnvironment {
|
||||||
|
runLocalTests(ctx, t, key)
|
||||||
|
} else {
|
||||||
|
runTestInContainer(ctx, t, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLocalTests(ctx context.Context, t *testing.T, key *keys.PrivateKey) {
|
||||||
|
runTests(ctx, t, key, localVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.24.0",
|
||||||
|
@ -64,15 +87,30 @@ func TestIntegration(t *testing.T) {
|
||||||
//"0.27.5",
|
//"0.27.5",
|
||||||
"latest",
|
"latest",
|
||||||
}
|
}
|
||||||
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for _, version := range versions {
|
for _, version := range versions {
|
||||||
ctx, cancel2 := context.WithCancel(rootCtx)
|
ctx, cancel := context.WithCancel(rootCtx)
|
||||||
|
|
||||||
aioContainer := createDockerContainer(ctx, t, aioImage+version)
|
aioContainer := createDockerContainer(ctx, t, aioImage+version)
|
||||||
cancel := runServer(ctx, t)
|
|
||||||
clientPool := getPool(ctx, t, key)
|
runTests(ctx, t, key, version)
|
||||||
|
|
||||||
|
err := aioContainer.Terminate(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
cancel()
|
||||||
|
<-ctx.Done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTests(ctx context.Context, t *testing.T, key *keys.PrivateKey, version string) {
|
||||||
|
node := testContainerNode
|
||||||
|
if version == localVersion {
|
||||||
|
node = testLocalNode
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel := runServer(ctx, t, node)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
clientPool := getPool(ctx, t, key, node)
|
||||||
cnrID := createContainer(ctx, t, clientPool, containerName)
|
cnrID := createContainer(ctx, t, clientPool, containerName)
|
||||||
restrictByEACL(ctx, t, clientPool, cnrID)
|
restrictByEACL(ctx, t, clientPool, cnrID)
|
||||||
|
|
||||||
|
@ -80,19 +118,12 @@ func TestIntegration(t *testing.T) {
|
||||||
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, 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, cnrID) })
|
||||||
|
|
||||||
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, clientPool, 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) })
|
||||||
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) })
|
||||||
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, cnrID) })
|
||||||
|
|
||||||
cancel()
|
|
||||||
err = aioContainer.Terminate(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
cancel2()
|
|
||||||
<-ctx.Done()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDockerContainer(ctx context.Context, t *testing.T, image string) testcontainers.Container {
|
func createDockerContainer(ctx context.Context, t *testing.T, image string) testcontainers.Container {
|
||||||
|
@ -112,10 +143,10 @@ func createDockerContainer(ctx context.Context, t *testing.T, image string) test
|
||||||
return aioC
|
return aioC
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServer(ctx context.Context, t *testing.T) context.CancelFunc {
|
func runServer(ctx context.Context, t *testing.T, node string) context.CancelFunc {
|
||||||
cancelCtx, cancel := context.WithCancel(ctx)
|
cancelCtx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
v := getDefaultConfig()
|
v := getDefaultConfig(node)
|
||||||
l := newLogger(v)
|
l := newLogger(v)
|
||||||
|
|
||||||
neofsAPI, err := newNeofsAPI(cancelCtx, l, v)
|
neofsAPI, err := newNeofsAPI(cancelCtx, l, v)
|
||||||
|
@ -145,9 +176,9 @@ func defaultHTTPClient() *http.Client {
|
||||||
return &http.Client{Timeout: 60 * time.Second}
|
return &http.Client{Timeout: 60 * time.Second}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultConfig() *viper.Viper {
|
func getDefaultConfig(node string) *viper.Viper {
|
||||||
v := config()
|
v := config()
|
||||||
v.SetDefault(cfgPeers+".0.address", testNode)
|
v.SetDefault(cfgPeers+".0.address", node)
|
||||||
v.SetDefault(cfgPeers+".0.weight", 1)
|
v.SetDefault(cfgPeers+".0.weight", 1)
|
||||||
v.SetDefault(cfgPeers+".0.priority", 1)
|
v.SetDefault(cfgPeers+".0.priority", 1)
|
||||||
v.SetDefault(restapi.FlagListenAddress, testListenAddress)
|
v.SetDefault(restapi.FlagListenAddress, testListenAddress)
|
||||||
|
@ -156,9 +187,9 @@ func getDefaultConfig() *viper.Viper {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPool(ctx context.Context, t *testing.T, key *keys.PrivateKey) *pool.Pool {
|
func getPool(ctx context.Context, t *testing.T, key *keys.PrivateKey, node string) *pool.Pool {
|
||||||
var prm pool.InitParameters
|
var prm pool.InitParameters
|
||||||
prm.AddNode(pool.NewNodeParam(1, testNode, 1))
|
prm.AddNode(pool.NewNodeParam(1, node, 1))
|
||||||
prm.SetKey(&key.PrivateKey)
|
prm.SetKey(&key.PrivateKey)
|
||||||
prm.SetHealthcheckTimeout(5 * time.Second)
|
prm.SetHealthcheckTimeout(5 * time.Second)
|
||||||
prm.SetNodeDialTimeout(5 * time.Second)
|
prm.SetNodeDialTimeout(5 * time.Second)
|
||||||
|
@ -204,7 +235,10 @@ func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnr
|
||||||
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/objects", bytes.NewReader(body))
|
query := make(url.Values)
|
||||||
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
|
||||||
|
request, err := http.NewRequest(http.MethodPut, testHost+"/v1/objects?"+query.Encode(), bytes.NewReader(body))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
request.Header.Add("X-Attribute-"+attrKey, attrValue)
|
request.Header.Add("X-Attribute-"+attrKey, attrValue)
|
||||||
|
@ -260,7 +294,10 @@ func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.I
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
|
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.String()+"/"+objID.String(), nil)
|
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)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
|
||||||
|
@ -295,7 +332,10 @@ func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *ci
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
|
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodDelete, testHost+"/v1/objects/"+cnrID.String()+"/"+objID.String(), nil)
|
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)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
|
||||||
|
@ -360,7 +400,10 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
|
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodDelete, testHost+"/v1/containers/"+cnrID.String(), nil)
|
query := make(url.Values)
|
||||||
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
|
||||||
|
request, err := http.NewRequest(http.MethodDelete, testHost+"/v1/containers/"+cnrID.String()+"?"+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)
|
||||||
|
@ -400,7 +443,10 @@ func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Po
|
||||||
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))
|
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))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
request = request.WithContext(ctx)
|
request = request.WithContext(ctx)
|
||||||
prepareCommonHeaders(request.Header, bearerToken)
|
prepareCommonHeaders(request.Header, bearerToken)
|
||||||
|
@ -521,24 +567,39 @@ func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearer *models.Bear
|
||||||
binaryData, err := base64.StdEncoding.DecodeString(*stokenResp.Token)
|
binaryData, err := base64.StdEncoding.DecodeString(*stokenResp.Token)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
signatureData := signData(t, key, binaryData)
|
var bt *handlers.BearerToken
|
||||||
signature := base64.StdEncoding.EncodeToString(signatureData)
|
if useWalletConnect {
|
||||||
|
bt = signTokenWalletConnect(t, key, binaryData)
|
||||||
bt := handlers.BearerToken{
|
} else {
|
||||||
Token: *stokenResp.Token,
|
bt = signToken(t, key, binaryData)
|
||||||
Signature: signature,
|
|
||||||
Key: hexPubKey,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("container token:\n%+v\n", bt)
|
fmt.Printf("container token:\n%+v\n", bt)
|
||||||
return &bt
|
return bt
|
||||||
}
|
}
|
||||||
|
|
||||||
func signData(t *testing.T, key *keys.PrivateKey, data []byte) []byte {
|
func signToken(t *testing.T, key *keys.PrivateKey, data []byte) *handlers.BearerToken {
|
||||||
h := sha512.Sum512(data)
|
h := sha512.Sum512(data)
|
||||||
x, y, err := ecdsa.Sign(rand.Reader, &key.PrivateKey, h[:])
|
x, y, err := ecdsa.Sign(rand.Reader, &key.PrivateKey, h[:])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return elliptic.Marshal(elliptic.P256(), x, y)
|
sign := elliptic.Marshal(elliptic.P256(), x, y)
|
||||||
|
|
||||||
|
return &handlers.BearerToken{
|
||||||
|
Token: base64.StdEncoding.EncodeToString(data),
|
||||||
|
Signature: base64.StdEncoding.EncodeToString(sign),
|
||||||
|
Key: hex.EncodeToString(key.PublicKey().Bytes()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func signTokenWalletConnect(t *testing.T, key *keys.PrivateKey, data []byte) *handlers.BearerToken {
|
||||||
|
sm, err := walletconnect.SignMessage(&key.PrivateKey, data[:])
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return &handlers.BearerToken{
|
||||||
|
Token: base64.StdEncoding.EncodeToString(data),
|
||||||
|
Signature: base64.StdEncoding.EncodeToString(append(sm.Data, sm.Salt...)),
|
||||||
|
Key: hex.EncodeToString(key.PublicKey().Bytes()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restContainerPut(ctx context.Context, t *testing.T, clientPool *pool.Pool) {
|
func restContainerPut(ctx context.Context, t *testing.T, clientPool *pool.Pool) {
|
||||||
|
@ -566,6 +627,7 @@ func restContainerPut(ctx context.Context, t *testing.T, clientPool *pool.Pool)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
query := reqURL.Query()
|
query := reqURL.Query()
|
||||||
query.Add("skip-native-name", "true")
|
query.Add("skip-native-name", "true")
|
||||||
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
reqURL.RawQuery = query.Encode()
|
reqURL.RawQuery = query.Encode()
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPut, reqURL.String(), bytes.NewReader(body))
|
request, err := http.NewRequest(http.MethodPut, reqURL.String(), bytes.NewReader(body))
|
||||||
|
|
|
@ -144,6 +144,9 @@ func init() {
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/signatureKeyParam"
|
"$ref": "#/parameters/signatureKeyParam"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/signatureScheme"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
|
@ -236,6 +239,9 @@ func init() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/signatureKeyParam"
|
"$ref": "#/parameters/signatureKeyParam"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/signatureScheme"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
@ -286,6 +292,9 @@ func init() {
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/signatureKeyParam"
|
"$ref": "#/parameters/signatureKeyParam"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/signatureScheme"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "EACL for container",
|
"description": "EACL for container",
|
||||||
"name": "eacl",
|
"name": "eacl",
|
||||||
|
@ -392,6 +401,9 @@ func init() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/signatureKeyParam"
|
"$ref": "#/parameters/signatureKeyParam"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/signatureScheme"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -436,6 +448,9 @@ func init() {
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/signatureKeyParam"
|
"$ref": "#/parameters/signatureKeyParam"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/signatureScheme"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/containerId"
|
"$ref": "#/parameters/containerId"
|
||||||
},
|
},
|
||||||
|
@ -841,6 +856,13 @@ func init() {
|
||||||
"name": "X-Bearer-Signature",
|
"name": "X-Bearer-Signature",
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
"signatureScheme": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use wallect connect signature scheme or not",
|
||||||
|
"name": "walletConnect",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
|
@ -997,6 +1019,13 @@ func init() {
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use wallect connect signature scheme or not",
|
||||||
|
"name": "walletConnect",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
|
@ -1097,6 +1126,13 @@ func init() {
|
||||||
"name": "X-Bearer-Signature-Key",
|
"name": "X-Bearer-Signature-Key",
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use wallect connect signature scheme or not",
|
||||||
|
"name": "walletConnect",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
@ -1159,6 +1195,13 @@ func init() {
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use wallect connect signature scheme or not",
|
||||||
|
"name": "walletConnect",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "EACL for container",
|
"description": "EACL for container",
|
||||||
"name": "eacl",
|
"name": "eacl",
|
||||||
|
@ -1277,6 +1320,13 @@ func init() {
|
||||||
"name": "X-Bearer-Signature-Key",
|
"name": "X-Bearer-Signature-Key",
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use wallect connect signature scheme or not",
|
||||||
|
"name": "walletConnect",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1329,6 +1379,13 @@ func init() {
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use wallect connect signature scheme or not",
|
||||||
|
"name": "walletConnect",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Base58 encoded container id",
|
"description": "Base58 encoded container id",
|
||||||
|
@ -1742,6 +1799,13 @@ func init() {
|
||||||
"name": "X-Bearer-Signature",
|
"name": "X-Bearer-Signature",
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
"signatureScheme": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use wallect connect signature scheme or not",
|
||||||
|
"name": "walletConnect",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
|
|
|
@ -9,17 +9,26 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-openapi/errors"
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/runtime"
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
"github.com/go-openapi/validate"
|
"github.com/go-openapi/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDeleteContainerParams creates a new DeleteContainerParams object
|
// NewDeleteContainerParams creates a new DeleteContainerParams object
|
||||||
//
|
// with the default values initialized.
|
||||||
// There are no default values defined in the spec.
|
|
||||||
func NewDeleteContainerParams() DeleteContainerParams {
|
func NewDeleteContainerParams() DeleteContainerParams {
|
||||||
|
|
||||||
return DeleteContainerParams{}
|
var (
|
||||||
|
// initialize parameters with default values
|
||||||
|
|
||||||
|
walletConnectDefault = bool(false)
|
||||||
|
)
|
||||||
|
|
||||||
|
return DeleteContainerParams{
|
||||||
|
WalletConnect: &walletConnectDefault,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteContainerParams contains all the bound params for the delete container operation
|
// DeleteContainerParams contains all the bound params for the delete container operation
|
||||||
|
@ -46,6 +55,11 @@ type DeleteContainerParams struct {
|
||||||
In: path
|
In: path
|
||||||
*/
|
*/
|
||||||
ContainerID string
|
ContainerID string
|
||||||
|
/*Use wallect connect signature scheme or not
|
||||||
|
In: query
|
||||||
|
Default: false
|
||||||
|
*/
|
||||||
|
WalletConnect *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||||
|
@ -57,6 +71,8 @@ func (o *DeleteContainerParams) BindRequest(r *http.Request, route *middleware.M
|
||||||
|
|
||||||
o.HTTPRequest = r
|
o.HTTPRequest = r
|
||||||
|
|
||||||
|
qs := runtime.Values(r.URL.Query())
|
||||||
|
|
||||||
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
@ -69,6 +85,11 @@ func (o *DeleteContainerParams) BindRequest(r *http.Request, route *middleware.M
|
||||||
if err := o.bindContainerID(rContainerID, rhkContainerID, route.Formats); err != nil {
|
if err := o.bindContainerID(rContainerID, rhkContainerID, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qWalletConnect, qhkWalletConnect, _ := qs.GetOK("walletConnect")
|
||||||
|
if err := o.bindWalletConnect(qWalletConnect, qhkWalletConnect, route.Formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
return errors.CompositeValidationError(res...)
|
return errors.CompositeValidationError(res...)
|
||||||
}
|
}
|
||||||
|
@ -128,3 +149,27 @@ func (o *DeleteContainerParams) bindContainerID(rawData []string, hasKey bool, f
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindWalletConnect binds and validates parameter WalletConnect from query.
|
||||||
|
func (o *DeleteContainerParams) bindWalletConnect(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
|
var raw string
|
||||||
|
if len(rawData) > 0 {
|
||||||
|
raw = rawData[len(rawData)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required: false
|
||||||
|
// AllowEmptyValue: false
|
||||||
|
|
||||||
|
if raw == "" { // empty values pass all other validations
|
||||||
|
// Default values have been previously initialized by NewDeleteContainerParams()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := swag.ConvertBool(raw)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InvalidType("walletConnect", "query", "bool", raw)
|
||||||
|
}
|
||||||
|
o.WalletConnect = &value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -9,17 +9,26 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-openapi/errors"
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/runtime"
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
"github.com/go-openapi/validate"
|
"github.com/go-openapi/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDeleteObjectParams creates a new DeleteObjectParams object
|
// NewDeleteObjectParams creates a new DeleteObjectParams object
|
||||||
//
|
// with the default values initialized.
|
||||||
// There are no default values defined in the spec.
|
|
||||||
func NewDeleteObjectParams() DeleteObjectParams {
|
func NewDeleteObjectParams() DeleteObjectParams {
|
||||||
|
|
||||||
return DeleteObjectParams{}
|
var (
|
||||||
|
// initialize parameters with default values
|
||||||
|
|
||||||
|
walletConnectDefault = bool(false)
|
||||||
|
)
|
||||||
|
|
||||||
|
return DeleteObjectParams{
|
||||||
|
WalletConnect: &walletConnectDefault,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObjectParams contains all the bound params for the delete object operation
|
// DeleteObjectParams contains all the bound params for the delete object operation
|
||||||
|
@ -51,6 +60,11 @@ type DeleteObjectParams struct {
|
||||||
In: path
|
In: path
|
||||||
*/
|
*/
|
||||||
ObjectID string
|
ObjectID string
|
||||||
|
/*Use wallect connect signature scheme or not
|
||||||
|
In: query
|
||||||
|
Default: false
|
||||||
|
*/
|
||||||
|
WalletConnect *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||||
|
@ -62,6 +76,8 @@ func (o *DeleteObjectParams) BindRequest(r *http.Request, route *middleware.Matc
|
||||||
|
|
||||||
o.HTTPRequest = r
|
o.HTTPRequest = r
|
||||||
|
|
||||||
|
qs := runtime.Values(r.URL.Query())
|
||||||
|
|
||||||
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
@ -79,6 +95,11 @@ func (o *DeleteObjectParams) BindRequest(r *http.Request, route *middleware.Matc
|
||||||
if err := o.bindObjectID(rObjectID, rhkObjectID, route.Formats); err != nil {
|
if err := o.bindObjectID(rObjectID, rhkObjectID, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qWalletConnect, qhkWalletConnect, _ := qs.GetOK("walletConnect")
|
||||||
|
if err := o.bindWalletConnect(qWalletConnect, qhkWalletConnect, route.Formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
return errors.CompositeValidationError(res...)
|
return errors.CompositeValidationError(res...)
|
||||||
}
|
}
|
||||||
|
@ -152,3 +173,27 @@ func (o *DeleteObjectParams) bindObjectID(rawData []string, hasKey bool, formats
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindWalletConnect binds and validates parameter WalletConnect from query.
|
||||||
|
func (o *DeleteObjectParams) bindWalletConnect(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
|
var raw string
|
||||||
|
if len(rawData) > 0 {
|
||||||
|
raw = rawData[len(rawData)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required: false
|
||||||
|
// AllowEmptyValue: false
|
||||||
|
|
||||||
|
if raw == "" { // empty values pass all other validations
|
||||||
|
// Default values have been previously initialized by NewDeleteObjectParams()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := swag.ConvertBool(raw)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InvalidType("walletConnect", "query", "bool", raw)
|
||||||
|
}
|
||||||
|
o.WalletConnect = &value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -9,17 +9,26 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-openapi/errors"
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/runtime"
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
"github.com/go-openapi/validate"
|
"github.com/go-openapi/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewGetObjectInfoParams creates a new GetObjectInfoParams object
|
// NewGetObjectInfoParams creates a new GetObjectInfoParams object
|
||||||
//
|
// with the default values initialized.
|
||||||
// There are no default values defined in the spec.
|
|
||||||
func NewGetObjectInfoParams() GetObjectInfoParams {
|
func NewGetObjectInfoParams() GetObjectInfoParams {
|
||||||
|
|
||||||
return GetObjectInfoParams{}
|
var (
|
||||||
|
// initialize parameters with default values
|
||||||
|
|
||||||
|
walletConnectDefault = bool(false)
|
||||||
|
)
|
||||||
|
|
||||||
|
return GetObjectInfoParams{
|
||||||
|
WalletConnect: &walletConnectDefault,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectInfoParams contains all the bound params for the get object info operation
|
// GetObjectInfoParams contains all the bound params for the get object info operation
|
||||||
|
@ -51,6 +60,11 @@ type GetObjectInfoParams struct {
|
||||||
In: path
|
In: path
|
||||||
*/
|
*/
|
||||||
ObjectID string
|
ObjectID string
|
||||||
|
/*Use wallect connect signature scheme or not
|
||||||
|
In: query
|
||||||
|
Default: false
|
||||||
|
*/
|
||||||
|
WalletConnect *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||||
|
@ -62,6 +76,8 @@ func (o *GetObjectInfoParams) BindRequest(r *http.Request, route *middleware.Mat
|
||||||
|
|
||||||
o.HTTPRequest = r
|
o.HTTPRequest = r
|
||||||
|
|
||||||
|
qs := runtime.Values(r.URL.Query())
|
||||||
|
|
||||||
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
@ -79,6 +95,11 @@ func (o *GetObjectInfoParams) BindRequest(r *http.Request, route *middleware.Mat
|
||||||
if err := o.bindObjectID(rObjectID, rhkObjectID, route.Formats); err != nil {
|
if err := o.bindObjectID(rObjectID, rhkObjectID, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qWalletConnect, qhkWalletConnect, _ := qs.GetOK("walletConnect")
|
||||||
|
if err := o.bindWalletConnect(qWalletConnect, qhkWalletConnect, route.Formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
return errors.CompositeValidationError(res...)
|
return errors.CompositeValidationError(res...)
|
||||||
}
|
}
|
||||||
|
@ -152,3 +173,27 @@ func (o *GetObjectInfoParams) bindObjectID(rawData []string, hasKey bool, format
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindWalletConnect binds and validates parameter WalletConnect from query.
|
||||||
|
func (o *GetObjectInfoParams) bindWalletConnect(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
|
var raw string
|
||||||
|
if len(rawData) > 0 {
|
||||||
|
raw = rawData[len(rawData)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required: false
|
||||||
|
// AllowEmptyValue: false
|
||||||
|
|
||||||
|
if raw == "" { // empty values pass all other validations
|
||||||
|
// Default values have been previously initialized by NewGetObjectInfoParams()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := swag.ConvertBool(raw)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InvalidType("walletConnect", "query", "bool", raw)
|
||||||
|
}
|
||||||
|
o.WalletConnect = &value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -14,17 +14,25 @@ import (
|
||||||
"github.com/go-openapi/runtime"
|
"github.com/go-openapi/runtime"
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
"github.com/go-openapi/validate"
|
"github.com/go-openapi/validate"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPutContainerEACLParams creates a new PutContainerEACLParams object
|
// NewPutContainerEACLParams creates a new PutContainerEACLParams object
|
||||||
//
|
// with the default values initialized.
|
||||||
// There are no default values defined in the spec.
|
|
||||||
func NewPutContainerEACLParams() PutContainerEACLParams {
|
func NewPutContainerEACLParams() PutContainerEACLParams {
|
||||||
|
|
||||||
return PutContainerEACLParams{}
|
var (
|
||||||
|
// initialize parameters with default values
|
||||||
|
|
||||||
|
walletConnectDefault = bool(false)
|
||||||
|
)
|
||||||
|
|
||||||
|
return PutContainerEACLParams{
|
||||||
|
WalletConnect: &walletConnectDefault,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutContainerEACLParams contains all the bound params for the put container e ACL operation
|
// PutContainerEACLParams contains all the bound params for the put container e ACL operation
|
||||||
|
@ -56,6 +64,11 @@ type PutContainerEACLParams struct {
|
||||||
In: body
|
In: body
|
||||||
*/
|
*/
|
||||||
Eacl *models.Eacl
|
Eacl *models.Eacl
|
||||||
|
/*Use wallect connect signature scheme or not
|
||||||
|
In: query
|
||||||
|
Default: false
|
||||||
|
*/
|
||||||
|
WalletConnect *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||||
|
@ -67,6 +80,8 @@ func (o *PutContainerEACLParams) BindRequest(r *http.Request, route *middleware.
|
||||||
|
|
||||||
o.HTTPRequest = r
|
o.HTTPRequest = r
|
||||||
|
|
||||||
|
qs := runtime.Values(r.URL.Query())
|
||||||
|
|
||||||
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
@ -107,6 +122,11 @@ func (o *PutContainerEACLParams) BindRequest(r *http.Request, route *middleware.
|
||||||
} else {
|
} else {
|
||||||
res = append(res, errors.Required("eacl", "body", ""))
|
res = append(res, errors.Required("eacl", "body", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qWalletConnect, qhkWalletConnect, _ := qs.GetOK("walletConnect")
|
||||||
|
if err := o.bindWalletConnect(qWalletConnect, qhkWalletConnect, route.Formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
return errors.CompositeValidationError(res...)
|
return errors.CompositeValidationError(res...)
|
||||||
}
|
}
|
||||||
|
@ -166,3 +186,27 @@ func (o *PutContainerEACLParams) bindContainerID(rawData []string, hasKey bool,
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindWalletConnect binds and validates parameter WalletConnect from query.
|
||||||
|
func (o *PutContainerEACLParams) bindWalletConnect(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
|
var raw string
|
||||||
|
if len(rawData) > 0 {
|
||||||
|
raw = rawData[len(rawData)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required: false
|
||||||
|
// AllowEmptyValue: false
|
||||||
|
|
||||||
|
if raw == "" { // empty values pass all other validations
|
||||||
|
// Default values have been previously initialized by NewPutContainerEACLParams()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := swag.ConvertBool(raw)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InvalidType("walletConnect", "query", "bool", raw)
|
||||||
|
}
|
||||||
|
o.WalletConnect = &value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -26,10 +26,13 @@ func NewPutContainerParams() PutContainerParams {
|
||||||
// initialize parameters with default values
|
// initialize parameters with default values
|
||||||
|
|
||||||
skipNativeNameDefault = bool(false)
|
skipNativeNameDefault = bool(false)
|
||||||
|
walletConnectDefault = bool(false)
|
||||||
)
|
)
|
||||||
|
|
||||||
return PutContainerParams{
|
return PutContainerParams{
|
||||||
SkipNativeName: &skipNativeNameDefault,
|
SkipNativeName: &skipNativeNameDefault,
|
||||||
|
|
||||||
|
WalletConnect: &walletConnectDefault,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +65,11 @@ type PutContainerParams struct {
|
||||||
Default: false
|
Default: false
|
||||||
*/
|
*/
|
||||||
SkipNativeName *bool
|
SkipNativeName *bool
|
||||||
|
/*Use wallect connect signature scheme or not
|
||||||
|
In: query
|
||||||
|
Default: false
|
||||||
|
*/
|
||||||
|
WalletConnect *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||||
|
@ -115,6 +123,11 @@ func (o *PutContainerParams) BindRequest(r *http.Request, route *middleware.Matc
|
||||||
if err := o.bindSkipNativeName(qSkipNativeName, qhkSkipNativeName, route.Formats); err != nil {
|
if err := o.bindSkipNativeName(qSkipNativeName, qhkSkipNativeName, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qWalletConnect, qhkWalletConnect, _ := qs.GetOK("walletConnect")
|
||||||
|
if err := o.bindWalletConnect(qWalletConnect, qhkWalletConnect, route.Formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
return errors.CompositeValidationError(res...)
|
return errors.CompositeValidationError(res...)
|
||||||
}
|
}
|
||||||
|
@ -184,3 +197,27 @@ func (o *PutContainerParams) bindSkipNativeName(rawData []string, hasKey bool, f
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindWalletConnect binds and validates parameter WalletConnect from query.
|
||||||
|
func (o *PutContainerParams) bindWalletConnect(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
|
var raw string
|
||||||
|
if len(rawData) > 0 {
|
||||||
|
raw = rawData[len(rawData)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required: false
|
||||||
|
// AllowEmptyValue: false
|
||||||
|
|
||||||
|
if raw == "" { // empty values pass all other validations
|
||||||
|
// Default values have been previously initialized by NewPutContainerParams()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := swag.ConvertBool(raw)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InvalidType("walletConnect", "query", "bool", raw)
|
||||||
|
}
|
||||||
|
o.WalletConnect = &value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -14,15 +14,23 @@ import (
|
||||||
"github.com/go-openapi/runtime"
|
"github.com/go-openapi/runtime"
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
"github.com/go-openapi/validate"
|
"github.com/go-openapi/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPutObjectParams creates a new PutObjectParams object
|
// NewPutObjectParams creates a new PutObjectParams object
|
||||||
//
|
// with the default values initialized.
|
||||||
// There are no default values defined in the spec.
|
|
||||||
func NewPutObjectParams() PutObjectParams {
|
func NewPutObjectParams() PutObjectParams {
|
||||||
|
|
||||||
return PutObjectParams{}
|
var (
|
||||||
|
// initialize parameters with default values
|
||||||
|
|
||||||
|
walletConnectDefault = bool(false)
|
||||||
|
)
|
||||||
|
|
||||||
|
return PutObjectParams{
|
||||||
|
WalletConnect: &walletConnectDefault,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectParams contains all the bound params for the put object operation
|
// PutObjectParams contains all the bound params for the put object operation
|
||||||
|
@ -49,6 +57,11 @@ type PutObjectParams struct {
|
||||||
In: body
|
In: body
|
||||||
*/
|
*/
|
||||||
Object PutObjectBody
|
Object PutObjectBody
|
||||||
|
/*Use wallect connect signature scheme or not
|
||||||
|
In: query
|
||||||
|
Default: false
|
||||||
|
*/
|
||||||
|
WalletConnect *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||||
|
@ -60,6 +73,8 @@ func (o *PutObjectParams) BindRequest(r *http.Request, route *middleware.Matched
|
||||||
|
|
||||||
o.HTTPRequest = r
|
o.HTTPRequest = r
|
||||||
|
|
||||||
|
qs := runtime.Values(r.URL.Query())
|
||||||
|
|
||||||
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
@ -95,6 +110,11 @@ func (o *PutObjectParams) BindRequest(r *http.Request, route *middleware.Matched
|
||||||
} else {
|
} else {
|
||||||
res = append(res, errors.Required("object", "body", ""))
|
res = append(res, errors.Required("object", "body", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qWalletConnect, qhkWalletConnect, _ := qs.GetOK("walletConnect")
|
||||||
|
if err := o.bindWalletConnect(qWalletConnect, qhkWalletConnect, route.Formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
return errors.CompositeValidationError(res...)
|
return errors.CompositeValidationError(res...)
|
||||||
}
|
}
|
||||||
|
@ -140,3 +160,27 @@ func (o *PutObjectParams) bindXBearerSignatureKey(rawData []string, hasKey bool,
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindWalletConnect binds and validates parameter WalletConnect from query.
|
||||||
|
func (o *PutObjectParams) bindWalletConnect(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
|
var raw string
|
||||||
|
if len(rawData) > 0 {
|
||||||
|
raw = rawData[len(rawData)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required: false
|
||||||
|
// AllowEmptyValue: false
|
||||||
|
|
||||||
|
if raw == "" { // empty values pass all other validations
|
||||||
|
// Default values have been previously initialized by NewPutObjectParams()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := swag.ConvertBool(raw)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InvalidType("walletConnect", "query", "bool", raw)
|
||||||
|
}
|
||||||
|
o.WalletConnect = &value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -64,7 +64,7 @@ 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 // indirect
|
github.com/nspcc-dev/neofs-crypto v0.3.0
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
github.com/nspcc-dev/rfc6979 v0.2.0 // 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
|
||||||
|
|
|
@ -2,8 +2,10 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
walletconnect "github.com/nspcc-dev/neofs-rest-gw/wallet-connect"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -37,7 +39,7 @@ func (a *API) PutContainers(params operations.PutContainerParams, principal *mod
|
||||||
Signature: params.XBearerSignature,
|
Signature: params.XBearerSignature,
|
||||||
Key: params.XBearerSignatureKey,
|
Key: params.XBearerSignatureKey,
|
||||||
}
|
}
|
||||||
stoken, err := prepareSessionToken(bt)
|
stoken, err := prepareSessionToken(bt, *params.WalletConnect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrapError(err)
|
return wrapError(err)
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ func (a *API) PutContainerEACL(params operations.PutContainerEACLParams, princip
|
||||||
Signature: params.XBearerSignature,
|
Signature: params.XBearerSignature,
|
||||||
Key: params.XBearerSignatureKey,
|
Key: params.XBearerSignatureKey,
|
||||||
}
|
}
|
||||||
stoken, err := prepareSessionToken(bt)
|
stoken, err := prepareSessionToken(bt, *params.WalletConnect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrapError(err)
|
return wrapError(err)
|
||||||
}
|
}
|
||||||
|
@ -183,7 +185,7 @@ func (a *API) DeleteContainer(params operations.DeleteContainerParams, principal
|
||||||
Signature: params.XBearerSignature,
|
Signature: params.XBearerSignature,
|
||||||
Key: params.XBearerSignatureKey,
|
Key: params.XBearerSignatureKey,
|
||||||
}
|
}
|
||||||
stoken, err := prepareSessionToken(bt)
|
stoken, err := prepareSessionToken(bt, *params.WalletConnect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.log.Error("failed parse session token", zap.Error(err))
|
a.log.Error("failed parse session token", zap.Error(err))
|
||||||
return operations.NewDeleteContainerBadRequest().WithPayload(NewError(err))
|
return operations.NewDeleteContainerBadRequest().WithPayload(NewError(err))
|
||||||
|
@ -344,10 +346,10 @@ func createContainer(ctx context.Context, p *pool.Pool, stoken *session.Token, p
|
||||||
return cnrID, nil
|
return cnrID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareSessionToken(bt *BearerToken) (*session.Token, error) {
|
func prepareSessionToken(bt *BearerToken, isWalletConnect bool) (*session.Token, error) {
|
||||||
stoken, err := GetSessionToken(bt.Token)
|
data, err := base64.StdEncoding.DecodeString(bt.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not fetch session token: %w", err)
|
return nil, fmt.Errorf("can't base64-decode bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := base64.StdEncoding.DecodeString(bt.Signature)
|
signature, err := base64.StdEncoding.DecodeString(bt.Signature)
|
||||||
|
@ -360,34 +362,32 @@ func prepareSessionToken(bt *BearerToken) (*session.Token, error) {
|
||||||
return nil, fmt.Errorf("couldn't fetch bearer token owner key: %w", err)
|
return nil, fmt.Errorf("couldn't fetch bearer token owner key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v2signature := new(refs.Signature)
|
|
||||||
v2signature.SetScheme(refs.ECDSA_SHA512)
|
|
||||||
v2signature.SetSign(signature)
|
|
||||||
v2signature.SetKey(ownerKey.Bytes())
|
|
||||||
stoken.ToV2().SetSignature(v2signature)
|
|
||||||
|
|
||||||
if !stoken.VerifySignature() {
|
|
||||||
err = fmt.Errorf("invalid signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
return stoken, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetSessionToken(auth string) (*session.Token, error) {
|
|
||||||
data, err := base64.StdEncoding.DecodeString(auth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't base64-decode bearer token: %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 bearer token: %w", err)
|
return nil, fmt.Errorf("can't unmarshal bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tkn := new(session.Token)
|
stoken := new(session.Token)
|
||||||
tkn.ToV2().SetBody(body)
|
stoken.ToV2().SetBody(body)
|
||||||
|
|
||||||
return tkn, nil
|
v2signature := new(refs.Signature)
|
||||||
|
v2signature.SetScheme(refs.ECDSA_SHA512)
|
||||||
|
if isWalletConnect {
|
||||||
|
v2signature.SetScheme(2)
|
||||||
|
}
|
||||||
|
v2signature.SetSign(signature)
|
||||||
|
v2signature.SetKey(ownerKey.Bytes())
|
||||||
|
stoken.ToV2().SetSignature(v2signature)
|
||||||
|
|
||||||
|
if isWalletConnect {
|
||||||
|
if !walletconnect.Verify((*ecdsa.PublicKey)(ownerKey), data, signature) {
|
||||||
|
return nil, fmt.Errorf("invalid signature")
|
||||||
|
}
|
||||||
|
} else if !stoken.VerifySignature() {
|
||||||
|
return nil, fmt.Errorf("invalid signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
return stoken, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapError(err error) middleware.Responder {
|
func wrapError(err error) middleware.Responder {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
"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"
|
||||||
|
walletconnect "github.com/nspcc-dev/neofs-rest-gw/wallet-connect"
|
||||||
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"
|
"github.com/nspcc-dev/neofs-sdk-go/object/address"
|
||||||
|
@ -24,7 +26,7 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr
|
||||||
errorResponse := operations.NewPutObjectBadRequest()
|
errorResponse := operations.NewPutObjectBadRequest()
|
||||||
ctx := params.HTTPRequest.Context()
|
ctx := params.HTTPRequest.Context()
|
||||||
|
|
||||||
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey)
|
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorResponse.WithPayload(models.Error(err.Error()))
|
return errorResponse.WithPayload(models.Error(err.Error()))
|
||||||
}
|
}
|
||||||
|
@ -61,6 +63,7 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr
|
||||||
|
|
||||||
objID, err := a.pool.PutObject(ctx, prmPut)
|
objID, err := a.pool.PutObject(ctx, prmPut)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
a.log.Error("put object", zap.Error(err))
|
||||||
return errorResponse.WithPayload(NewError(err))
|
return errorResponse.WithPayload(NewError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +85,7 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo
|
||||||
return errorResponse.WithPayload("invalid address")
|
return errorResponse.WithPayload("invalid address")
|
||||||
}
|
}
|
||||||
|
|
||||||
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey)
|
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorResponse.WithPayload(NewError(err))
|
return errorResponse.WithPayload(NewError(err))
|
||||||
}
|
}
|
||||||
|
@ -123,7 +126,7 @@ func (a *API) DeleteObject(params operations.DeleteObjectParams, principal *mode
|
||||||
return errorResponse.WithPayload("invalid address")
|
return errorResponse.WithPayload("invalid address")
|
||||||
}
|
}
|
||||||
|
|
||||||
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey)
|
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.log.Error("failed to get bearer token", zap.Error(err))
|
a.log.Error("failed to get bearer token", zap.Error(err))
|
||||||
return errorResponse.WithPayload(NewError(err))
|
return errorResponse.WithPayload(NewError(err))
|
||||||
|
@ -158,20 +161,20 @@ func parseAddress(containerID, objectID string) (*address.Address, error) {
|
||||||
return addr, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBearerToken(token *models.Principal, signature, key string) (*token.BearerToken, error) {
|
func getBearerToken(token *models.Principal, signature, key string, isWalletConnect bool) (*token.BearerToken, error) {
|
||||||
bt := &BearerToken{
|
bt := &BearerToken{
|
||||||
Token: string(*token),
|
Token: string(*token),
|
||||||
Signature: signature,
|
Signature: signature,
|
||||||
Key: key,
|
Key: key,
|
||||||
}
|
}
|
||||||
|
|
||||||
return prepareBearerToken(bt)
|
return prepareBearerToken(bt, isWalletConnect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareBearerToken(bt *BearerToken) (*token.BearerToken, error) {
|
func prepareBearerToken(bt *BearerToken, isWalletConnect bool) (*token.BearerToken, error) {
|
||||||
btoken, err := parseBearerToken(bt.Token)
|
data, err := base64.StdEncoding.DecodeString(bt.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not fetch bearer token: %w", err)
|
return nil, fmt.Errorf("can't base64-decode bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := base64.StdEncoding.DecodeString(bt.Signature)
|
signature, err := base64.StdEncoding.DecodeString(bt.Signature)
|
||||||
|
@ -184,28 +187,30 @@ func prepareBearerToken(bt *BearerToken) (*token.BearerToken, error) {
|
||||||
return nil, fmt.Errorf("couldn't fetch bearer token owner key: %w", err)
|
return nil, fmt.Errorf("couldn't fetch bearer token owner key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v2signature := new(refs.Signature)
|
|
||||||
v2signature.SetScheme(refs.ECDSA_SHA512)
|
|
||||||
v2signature.SetSign(signature)
|
|
||||||
v2signature.SetKey(ownerKey.Bytes())
|
|
||||||
btoken.ToV2().SetSignature(v2signature)
|
|
||||||
|
|
||||||
return btoken, btoken.VerifySignature()
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBearerToken(auth string) (*token.BearerToken, error) {
|
|
||||||
data, err := base64.StdEncoding.DecodeString(auth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't base64-decode bearer token: %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 nil, fmt.Errorf("can't unmarshal bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tkn := new(token.BearerToken)
|
btoken := new(token.BearerToken)
|
||||||
tkn.ToV2().SetBody(body)
|
btoken.ToV2().SetBody(body)
|
||||||
|
|
||||||
return tkn, nil
|
v2signature := new(refs.Signature)
|
||||||
|
v2signature.SetScheme(refs.ECDSA_SHA512)
|
||||||
|
if isWalletConnect {
|
||||||
|
v2signature.SetScheme(2)
|
||||||
|
}
|
||||||
|
v2signature.SetSign(signature)
|
||||||
|
v2signature.SetKey(ownerKey.Bytes())
|
||||||
|
btoken.ToV2().SetSignature(v2signature)
|
||||||
|
|
||||||
|
if isWalletConnect {
|
||||||
|
if !walletconnect.Verify((*ecdsa.PublicKey)(ownerKey), data, signature) {
|
||||||
|
return nil, fmt.Errorf("invalid signature")
|
||||||
|
}
|
||||||
|
} else if err = btoken.VerifySignature(); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
return btoken, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,12 @@ parameters:
|
||||||
description: Hex encoded the public part of the key that signed the bearer token
|
description: Hex encoded the public part of the key that signed the bearer token
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
|
signatureScheme:
|
||||||
|
in: query
|
||||||
|
name: walletConnect
|
||||||
|
description: Use wallect connect signature scheme or not
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
containerId:
|
containerId:
|
||||||
in: path
|
in: path
|
||||||
name: containerId
|
name: containerId
|
||||||
|
@ -91,6 +97,7 @@ paths:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/parameters/signatureParam'
|
- $ref: '#/parameters/signatureParam'
|
||||||
- $ref: '#/parameters/signatureKeyParam'
|
- $ref: '#/parameters/signatureKeyParam'
|
||||||
|
- $ref: '#/parameters/signatureScheme'
|
||||||
put:
|
put:
|
||||||
operationId: putObject
|
operationId: putObject
|
||||||
summary: Upload object to NeoFS
|
summary: Upload object to NeoFS
|
||||||
|
@ -144,6 +151,7 @@ paths:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/parameters/signatureParam'
|
- $ref: '#/parameters/signatureParam'
|
||||||
- $ref: '#/parameters/signatureKeyParam'
|
- $ref: '#/parameters/signatureKeyParam'
|
||||||
|
- $ref: '#/parameters/signatureScheme'
|
||||||
- $ref: '#/parameters/containerId'
|
- $ref: '#/parameters/containerId'
|
||||||
- $ref: '#/parameters/objectId'
|
- $ref: '#/parameters/objectId'
|
||||||
get:
|
get:
|
||||||
|
@ -176,6 +184,7 @@ paths:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/parameters/signatureParam'
|
- $ref: '#/parameters/signatureParam'
|
||||||
- $ref: '#/parameters/signatureKeyParam'
|
- $ref: '#/parameters/signatureKeyParam'
|
||||||
|
- $ref: '#/parameters/signatureScheme'
|
||||||
- in: query
|
- in: query
|
||||||
name: skip-native-name
|
name: skip-native-name
|
||||||
description: Provide this parameter to skip registration container name in NNS service
|
description: Provide this parameter to skip registration container name in NNS service
|
||||||
|
@ -271,6 +280,7 @@ paths:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/parameters/signatureParam'
|
- $ref: '#/parameters/signatureParam'
|
||||||
- $ref: '#/parameters/signatureKeyParam'
|
- $ref: '#/parameters/signatureKeyParam'
|
||||||
|
- $ref: '#/parameters/signatureScheme'
|
||||||
responses:
|
responses:
|
||||||
204:
|
204:
|
||||||
description: Successul deletion
|
description: Successul deletion
|
||||||
|
@ -287,6 +297,7 @@ paths:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/parameters/signatureParam'
|
- $ref: '#/parameters/signatureParam'
|
||||||
- $ref: '#/parameters/signatureKeyParam'
|
- $ref: '#/parameters/signatureKeyParam'
|
||||||
|
- $ref: '#/parameters/signatureScheme'
|
||||||
- in: body
|
- in: body
|
||||||
name: eacl
|
name: eacl
|
||||||
required: true
|
required: true
|
||||||
|
|
149
wallet-connect/wallet_connect.go
Normal file
149
wallet-connect/wallet_connect.go
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
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(len(salt)+len(msg)))
|
||||||
|
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
|
||||||
|
}
|
50
wallet-connect/wallet_connect_test.go
Normal file
50
wallet-connect/wallet_connect_test.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package walletconnect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/hex"
|
||||||
|
"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/handlers"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const devenvPrivateKey = "1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb"
|
||||||
|
|
||||||
|
func TestSign(t *testing.T) {
|
||||||
|
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pubKeyHex := hex.EncodeToString(key.PublicKey().Bytes())
|
||||||
|
|
||||||
|
b := &models.Bearer{
|
||||||
|
Object: []*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 := handlers.ToNativeObjectToken(b)
|
||||||
|
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)
|
||||||
|
}
|
Loading…
Reference in a new issue