2022-04-11 09:35:06 +00:00
package main
import (
"bytes"
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
2022-04-13 08:41:04 +00:00
"net/url"
2022-04-11 09:35:06 +00:00
"strconv"
"testing"
"time"
2022-12-15 10:03:54 +00:00
"github.com/TrueCloudLab/frostfs-rest-gw/gen/models"
"github.com/TrueCloudLab/frostfs-rest-gw/gen/restapi"
"github.com/TrueCloudLab/frostfs-rest-gw/gen/restapi/operations"
"github.com/TrueCloudLab/frostfs-rest-gw/handlers"
"github.com/TrueCloudLab/frostfs-rest-gw/internal/util"
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
"github.com/TrueCloudLab/frostfs-sdk-go/container"
"github.com/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
neofsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
"github.com/TrueCloudLab/frostfs-sdk-go/object"
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/TrueCloudLab/frostfs-sdk-go/pool"
"github.com/TrueCloudLab/frostfs-sdk-go/user"
2022-04-11 09:35:06 +00:00
"github.com/go-openapi/loads"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)
const (
devenvPrivateKey = "1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb"
testListenAddress = "localhost:8082"
testHost = "http://" + testListenAddress
2022-04-15 07:03:00 +00:00
testContainerNode = "localhost:8080"
testLocalNode = "s01.neofs.devenv:8080"
2022-04-13 15:23:03 +00:00
containerName = "test-container"
2022-04-15 07:03:00 +00:00
localVersion = "local"
2022-04-11 09:35:06 +00:00
2022-04-15 07:03:00 +00:00
walletConnectQuery = "walletConnect"
2022-08-19 06:44:58 +00:00
fullBearerQuery = "fullBearer"
2022-04-14 08:53:13 +00:00
// XBearerSignature header contains base64 encoded signature of the token body.
XBearerSignature = "X-Bearer-Signature"
// XBearerSignatureKey header contains hex encoded public key that corresponds the signature of the token body.
XBearerSignatureKey = "X-Bearer-Signature-Key"
2022-06-09 09:59:02 +00:00
// XBearerOwnerID header contains owner id (wallet address) that corresponds the signature of the token body.
XBearerOwnerID = "X-Bearer-Owner-Id"
2022-08-19 06:44:58 +00:00
// XBearerForAllUsers header specifies if we want all users can use token or only specific gate.
XBearerForAllUsers = "X-Bearer-For-All-Users"
2022-04-15 07:03:00 +00:00
2022-06-09 09:59:02 +00:00
// tests configuration.
2022-07-12 08:01:21 +00:00
useWalletConnect = true
2022-07-07 13:33:36 +00:00
useLocalEnvironment = false
2022-04-11 09:35:06 +00:00
)
func TestIntegration ( t * testing . T ) {
2022-04-15 07:03:00 +00:00
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 ) {
2022-04-11 09:35:06 +00:00
aioImage := "nspccdev/neofs-aio-testcontainer:"
2022-04-13 08:41:04 +00:00
versions := [ ] string {
2022-07-12 08:01:21 +00:00
"0.29.0" ,
2022-10-18 09:49:06 +00:00
"0.30.0" ,
"0.32.0" ,
"latest" ,
2022-04-13 08:41:04 +00:00
}
2022-04-11 09:35:06 +00:00
for _ , version := range versions {
2022-04-15 07:03:00 +00:00
ctx , cancel := context . WithCancel ( rootCtx )
2022-04-11 09:35:06 +00:00
aioContainer := createDockerContainer ( ctx , t , aioImage + version )
2022-04-15 07:03:00 +00:00
runTests ( ctx , t , key , version )
err := aioContainer . Terminate ( ctx )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-15 07:03:00 +00:00
cancel ( )
2022-04-11 09:35:06 +00:00
<- ctx . Done ( )
}
}
2022-04-15 07:03:00 +00:00
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 ( )
2022-07-12 08:01:21 +00:00
var owner user . ID
user . IDFromKey ( & owner , key . PrivateKey . PublicKey )
2022-04-15 07:03:00 +00:00
clientPool := getPool ( ctx , t , key , node )
2022-07-12 08:01:21 +00:00
cnrID := createContainer ( ctx , t , clientPool , owner , containerName )
2022-04-15 07:03:00 +00:00
restrictByEACL ( ctx , t , clientPool , cnrID )
2022-07-07 09:02:05 +00:00
t . Run ( "rest auth several tokens " + version , func ( t * testing . T ) { authTokens ( ctx , t ) } )
2022-07-07 14:24:41 +00:00
t . Run ( "rest check mix tokens up " + version , func ( t * testing . T ) { mixTokens ( ctx , t , cnrID ) } )
2022-08-18 14:35:53 +00:00
t . Run ( "rest form full binary bearer " + version , func ( t * testing . T ) { formFullBinaryBearer ( ctx , t ) } )
2022-07-07 09:02:05 +00:00
2022-04-15 07:03:00 +00:00
t . Run ( "rest put object " + version , func ( t * testing . T ) { restObjectPut ( ctx , t , clientPool , cnrID ) } )
2022-07-12 08:01:21 +00:00
t . Run ( "rest get object " + version , func ( t * testing . T ) { restObjectGet ( ctx , t , clientPool , & owner , cnrID ) } )
2022-10-26 09:40:17 +00:00
t . Run ( "rest get object unauthenticated " + version , func ( t * testing . T ) { restObjectGetUnauthenticated ( ctx , t , clientPool , & owner , cnrID ) } )
2022-08-19 06:44:58 +00:00
t . Run ( "rest get object full bearer " + version , func ( t * testing . T ) { restObjectGetFullBearer ( ctx , t , clientPool , & owner , cnrID ) } )
2022-07-12 08:01:21 +00:00
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 ) } )
2022-04-15 07:03:00 +00:00
2022-07-07 13:33:36 +00:00
t . Run ( "rest put container invalid " + version , func ( t * testing . T ) { restContainerPutInvalid ( ctx , t ) } )
2022-04-15 07:03:00 +00:00
t . Run ( "rest put container " + version , func ( t * testing . T ) { restContainerPut ( ctx , t , clientPool ) } )
2022-07-12 08:01:21 +00:00
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 ) } )
2022-04-15 07:03:00 +00:00
t . Run ( "rest get container eacl " + version , func ( t * testing . T ) { restContainerEACLGet ( ctx , t , clientPool , cnrID ) } )
2022-07-12 08:01:21 +00:00
t . Run ( "rest list containers " + version , func ( t * testing . T ) { restContainerList ( ctx , t , clientPool , owner , cnrID ) } )
2022-04-15 07:03:00 +00:00
}
2022-04-11 09:35:06 +00:00
func createDockerContainer ( ctx context . Context , t * testing . T , image string ) testcontainers . Container {
req := testcontainers . ContainerRequest {
Image : image ,
WaitingFor : wait . NewLogStrategy ( "aio container started" ) . WithStartupTimeout ( 30 * time . Second ) ,
Name : "aio" ,
Hostname : "aio" ,
NetworkMode : "host" ,
}
aioC , err := testcontainers . GenericContainer ( ctx , testcontainers . GenericContainerRequest {
ContainerRequest : req ,
Started : true ,
} )
require . NoError ( t , err )
return aioC
}
2022-04-15 07:03:00 +00:00
func runServer ( ctx context . Context , t * testing . T , node string ) context . CancelFunc {
2022-04-11 09:35:06 +00:00
cancelCtx , cancel := context . WithCancel ( ctx )
2022-04-15 07:03:00 +00:00
v := getDefaultConfig ( node )
2022-04-11 09:35:06 +00:00
l := newLogger ( v )
neofsAPI , err := newNeofsAPI ( cancelCtx , l , v )
require . NoError ( t , err )
swaggerSpec , err := loads . Analyzed ( restapi . SwaggerJSON , "" )
require . NoError ( t , err )
2022-12-15 10:03:54 +00:00
api := operations . NewFrostfsRestGwAPI ( swaggerSpec )
2022-04-11 09:35:06 +00:00
server := restapi . NewServer ( api , serverConfig ( v ) )
server . ConfigureAPI ( neofsAPI . Configure )
go func ( ) {
err := server . Serve ( )
require . NoError ( t , err )
} ( )
return func ( ) {
cancel ( )
err := server . Shutdown ( )
require . NoError ( t , err )
}
}
2022-04-13 13:46:49 +00:00
func defaultHTTPClient ( ) * http . Client {
return & http . Client { Timeout : 60 * time . Second }
}
2022-04-15 07:03:00 +00:00
func getDefaultConfig ( node string ) * viper . Viper {
2022-04-11 09:35:06 +00:00
v := config ( )
2022-04-15 07:03:00 +00:00
v . SetDefault ( cfgPeers + ".0.address" , node )
2022-04-11 09:35:06 +00:00
v . SetDefault ( cfgPeers + ".0.weight" , 1 )
v . SetDefault ( cfgPeers + ".0.priority" , 1 )
2022-10-21 13:53:30 +00:00
v . SetDefault ( cfgServerSection + restapi . FlagListenAddress , testListenAddress )
v . SetDefault ( cfgServerSection + restapi . FlagWriteTimeout , 60 * time . Second )
2022-04-11 09:35:06 +00:00
return v
}
2022-04-15 07:03:00 +00:00
func getPool ( ctx context . Context , t * testing . T , key * keys . PrivateKey , node string ) * pool . Pool {
2022-04-11 09:35:06 +00:00
var prm pool . InitParameters
2022-04-15 07:03:00 +00:00
prm . AddNode ( pool . NewNodeParam ( 1 , node , 1 ) )
2022-04-11 09:35:06 +00:00
prm . SetKey ( & key . PrivateKey )
prm . SetHealthcheckTimeout ( 5 * time . Second )
prm . SetNodeDialTimeout ( 5 * time . Second )
clientPool , err := pool . NewPool ( prm )
require . NoError ( t , err )
err = clientPool . Dial ( ctx )
require . NoError ( t , err )
return clientPool
}
2022-04-21 11:41:50 +00:00
func getRestrictBearerRecords ( ) [ ] * models . Record {
return [ ] * models . Record {
formRestrictRecord ( models . OperationGET ) ,
formRestrictRecord ( models . OperationHEAD ) ,
formRestrictRecord ( models . OperationPUT ) ,
formRestrictRecord ( models . OperationDELETE ) ,
formRestrictRecord ( models . OperationSEARCH ) ,
formRestrictRecord ( models . OperationRANGE ) ,
formRestrictRecord ( models . OperationRANGEHASH ) ,
}
}
func formRestrictRecord ( op models . Operation ) * models . Record {
return & models . Record {
Operation : models . NewOperation ( op ) ,
Action : models . NewAction ( models . ActionDENY ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { {
Role : models . NewRole ( models . RoleOTHERS ) ,
Keys : [ ] string { } ,
} } }
}
2022-07-07 09:02:05 +00:00
func authTokens ( ctx context . Context , t * testing . T ) {
bearers := [ ] * models . Bearer {
{
Name : "all-object" ,
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 { } ,
} } ,
} } ,
} ,
{
Name : "put-container" ,
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbPUT ) ,
} ,
} ,
{
Name : "seteacl-container" ,
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbSETEACL ) ,
} ,
} ,
{
Name : "delete-container" ,
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbDELETE ) ,
} ,
} ,
}
httpClient := defaultHTTPClient ( )
2022-08-19 06:44:58 +00:00
makeAuthTokenRequest ( ctx , t , bearers , httpClient , false )
2022-07-07 09:02:05 +00:00
}
2022-07-12 08:01:21 +00:00
func mixTokens ( ctx context . Context , t * testing . T , cnrID cid . ID ) {
2022-07-07 14:24:41 +00:00
bearers := [ ] * models . Bearer {
{
Name : "all-object" ,
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 { } ,
} } ,
} } ,
} ,
{
Name : "put-container" ,
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbPUT ) ,
} ,
} ,
{
Name : "seteacl-container" ,
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbSETEACL ) ,
} ,
} ,
}
httpClient := defaultHTTPClient ( )
2022-08-19 06:44:58 +00:00
tokens := makeAuthTokenRequest ( ctx , t , bearers , httpClient , false )
2022-07-07 14:24:41 +00:00
objectToken := tokens [ 0 ]
containerPutToken := tokens [ 1 ]
containerSetEACLToken := tokens [ 2 ]
// check reject object token when container tokens is required
checkPutContainerWithError ( t , httpClient , objectToken )
// check reject wrong verb container token
checkPutContainerWithError ( t , httpClient , containerSetEACLToken )
// check reject wrong verb container token
checkDeleteContainerWithError ( t , httpClient , cnrID , containerSetEACLToken )
// check reject wrong verb container token
checkSetEACLContainerWithError ( t , httpClient , cnrID , containerPutToken )
// check reject container token when object tokens is required
checkPutObjectWithError ( t , httpClient , cnrID , containerSetEACLToken )
}
2022-08-18 14:35:53 +00:00
func formFullBinaryBearer ( ctx context . Context , t * testing . T ) {
bearers := [ ] * models . Bearer {
{
Name : "all-object" ,
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 { } ,
} } ,
} } ,
} ,
{
Name : "put-container" ,
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbPUT ) ,
} ,
} ,
}
httpClient := defaultHTTPClient ( )
2022-08-19 06:44:58 +00:00
tokens := makeAuthTokenRequest ( ctx , t , bearers , httpClient , false )
2022-08-18 14:35:53 +00:00
objectToken := tokens [ 0 ]
containerPutToken := tokens [ 1 ]
query := make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
// check that container token isn't valid
request , err := http . NewRequest ( http . MethodGet , testHost + "/v1/auth/bearer?" + query . Encode ( ) , nil )
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , containerPutToken )
checkGWErrorResponse ( t , httpClient , request )
// check that object bearer token is valid
request , err = http . NewRequest ( http . MethodGet , testHost + "/v1/auth/bearer?" + query . Encode ( ) , nil )
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , objectToken )
resp := & models . BinaryBearer { }
doRequest ( t , httpClient , request , http . StatusOK , resp )
actualTokenRaw , err := base64 . StdEncoding . DecodeString ( * resp . Token )
require . NoError ( t , err )
var actualToken bearer . Token
err = actualToken . Unmarshal ( actualTokenRaw )
require . NoError ( t , err )
require . True ( t , actualToken . VerifySignature ( ) )
require . Len ( t , actualToken . EACLTable ( ) . Records ( ) , 1 )
actualRecord := actualToken . EACLTable ( ) . Records ( ) [ 0 ]
require . Equal ( t , eacl . OperationPut , actualRecord . Operation ( ) )
require . Equal ( t , eacl . ActionAllow , actualRecord . Action ( ) )
require . Empty ( t , actualRecord . Filters ( ) )
require . Len ( t , actualRecord . Targets ( ) , 1 )
actualTarget := actualRecord . Targets ( ) [ 0 ]
require . Empty ( t , actualTarget . BinaryKeys ( ) )
require . Equal ( t , eacl . RoleOthers , actualTarget . Role ( ) )
}
2022-07-07 14:24:41 +00:00
func checkPutContainerWithError ( t * testing . T , httpClient * http . Client , token * handlers . BearerToken ) {
reqURL , err := url . Parse ( testHost + "/v1/containers" )
require . NoError ( t , err )
2022-07-19 14:48:28 +00:00
body , err := json . Marshal ( & models . ContainerPutInfo { ContainerName : "container" } )
2022-07-07 14:24:41 +00:00
require . NoError ( t , err )
request , err := http . NewRequest ( http . MethodPut , reqURL . String ( ) , bytes . NewReader ( body ) )
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , token )
checkGWErrorResponse ( t , httpClient , request )
}
2022-07-12 08:01:21 +00:00
func checkDeleteContainerWithError ( t * testing . T , httpClient * http . Client , cnrID cid . ID , token * handlers . BearerToken ) {
reqURL , err := url . Parse ( testHost + "/v1/containers/" + cnrID . EncodeToString ( ) )
2022-07-07 14:24:41 +00:00
require . NoError ( t , err )
request , err := http . NewRequest ( http . MethodDelete , reqURL . String ( ) , nil )
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , token )
checkGWErrorResponse ( t , httpClient , request )
}
2022-07-12 08:01:21 +00:00
func checkSetEACLContainerWithError ( t * testing . T , httpClient * http . Client , cnrID cid . ID , token * handlers . BearerToken ) {
2022-07-07 14:24:41 +00:00
req := models . Eacl { Records : [ ] * models . Record { } }
body , err := json . Marshal ( & req )
require . NoError ( t , err )
2022-07-12 08:01:21 +00:00
request , err := http . NewRequest ( http . MethodPut , testHost + "/v1/containers/" + cnrID . EncodeToString ( ) + "/eacl" , bytes . NewReader ( body ) )
2022-07-07 14:24:41 +00:00
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , token )
checkGWErrorResponse ( t , httpClient , request )
}
2022-07-12 08:01:21 +00:00
func checkPutObjectWithError ( t * testing . T , httpClient * http . Client , cnrID cid . ID , token * handlers . BearerToken ) {
2022-07-07 14:24:41 +00:00
req := & models . ObjectUpload {
2022-07-12 08:01:21 +00:00
ContainerID : util . NewString ( cnrID . EncodeToString ( ) ) ,
2022-07-07 14:24:41 +00:00
FileName : util . NewString ( "newFile.txt" ) ,
Payload : base64 . StdEncoding . EncodeToString ( [ ] byte ( "content" ) ) ,
}
body , err := json . Marshal ( req )
require . NoError ( t , err )
request , err := http . NewRequest ( http . MethodPut , testHost + "/v1/objects?" , bytes . NewReader ( body ) )
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , token )
checkGWErrorResponse ( t , httpClient , request )
}
func checkGWErrorResponse ( t * testing . T , httpClient * http . Client , request * http . Request ) {
resp := & models . ErrorResponse { }
doRequest ( t , httpClient , request , http . StatusBadRequest , resp )
require . Equal ( t , int64 ( 0 ) , resp . Code )
require . Equal ( t , models . ErrorTypeGW , * resp . Type )
}
2022-07-12 08:01:21 +00:00
func restObjectPut ( ctx context . Context , t * testing . T , clientPool * pool . Pool , cnrID cid . ID ) {
2022-04-13 13:46:49 +00:00
bearer := & models . Bearer {
2022-04-11 09:35:06 +00:00
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 { } ,
} } ,
} } ,
}
2022-04-21 11:41:50 +00:00
bearer . Object = append ( bearer . Object , getRestrictBearerRecords ( ) ... )
2022-04-11 09:35:06 +00:00
2022-04-13 13:46:49 +00:00
httpClient := defaultHTTPClient ( )
2022-08-19 06:44:58 +00:00
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearer } , httpClient , false )
2022-07-07 09:02:05 +00:00
bearerToken := bearerTokens [ 0 ]
2022-04-11 09:35:06 +00:00
content := "content of file"
attrKey , attrValue := "User-Attribute" , "user value"
attributes := map [ string ] string {
object . AttributeFileName : "newFile.txt" ,
attrKey : attrValue ,
}
2022-04-20 14:52:41 +00:00
req := & models . ObjectUpload {
2022-07-12 08:01:21 +00:00
ContainerID : util . NewString ( cnrID . EncodeToString ( ) ) ,
2022-04-29 06:39:24 +00:00
FileName : util . NewString ( "newFile.txt" ) ,
2022-04-11 09:35:06 +00:00
Payload : base64 . StdEncoding . EncodeToString ( [ ] byte ( content ) ) ,
2022-04-20 14:52:41 +00:00
Attributes : [ ] * models . Attribute { {
Key : & attrKey ,
Value : & attrValue ,
} } ,
2022-04-11 09:35:06 +00:00
}
2022-04-20 14:52:41 +00:00
body , err := json . Marshal ( req )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-15 07:03:00 +00:00
query := make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
request , err := http . NewRequest ( http . MethodPut , testHost + "/v1/objects?" + query . Encode ( ) , bytes . NewReader ( body ) )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 13:46:49 +00:00
prepareCommonHeaders ( request . Header , bearerToken )
2022-04-11 09:35:06 +00:00
2022-04-18 08:30:34 +00:00
addr := & models . Address { }
2022-04-13 13:46:49 +00:00
doRequest ( t , httpClient , request , http . StatusOK , addr )
2022-04-11 09:35:06 +00:00
var CID cid . ID
2022-07-12 08:01:21 +00:00
err = CID . DecodeString ( * addr . ContainerID )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 13:46:49 +00:00
var id oid . ID
2022-07-12 08:01:21 +00:00
err = id . DecodeString ( * addr . ObjectID )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-07-12 08:01:21 +00:00
var objectAddress oid . Address
objectAddress . SetContainer ( CID )
objectAddress . SetObject ( id )
2022-04-11 09:35:06 +00:00
var prm pool . PrmObjectGet
2022-07-12 08:01:21 +00:00
prm . SetAddress ( objectAddress )
2022-04-11 09:35:06 +00:00
res , err := clientPool . GetObject ( ctx , prm )
require . NoError ( t , err )
2022-04-13 13:46:49 +00:00
payload := bytes . NewBuffer ( nil )
2022-04-11 09:35:06 +00:00
_ , err = io . Copy ( payload , res . Payload )
require . NoError ( t , err )
require . Equal ( t , content , payload . String ( ) )
for _ , attribute := range res . Header . Attributes ( ) {
require . Equal ( t , attributes [ attribute . Key ( ) ] , attribute . Value ( ) , attribute . Key ( ) )
}
}
2022-07-12 08:01:21 +00:00
func restObjectGet ( ctx context . Context , t * testing . T , p * pool . Pool , ownerID * user . ID , cnrID cid . ID ) {
2022-04-20 14:10:43 +00:00
content := [ ] byte ( "some content" )
2022-04-13 13:46:49 +00:00
attributes := map [ string ] string {
object . AttributeFileName : "get-obj-name" ,
"user-attribute" : "user value" ,
}
2022-07-12 08:01:21 +00:00
objID := createObject ( ctx , t , p , ownerID , cnrID , attributes , content )
2022-04-13 13:46:49 +00:00
bearer := & models . Bearer {
2022-04-21 11:41:50 +00:00
Object : [ ] * models . Record {
{
Operation : models . NewOperation ( models . OperationHEAD ) ,
Action : models . NewAction ( models . ActionALLOW ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { {
Role : models . NewRole ( models . RoleOTHERS ) ,
Keys : [ ] string { } ,
} } ,
} ,
{
Operation : models . NewOperation ( models . OperationRANGE ) ,
Action : models . NewAction ( models . ActionALLOW ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { {
Role : models . NewRole ( models . RoleOTHERS ) ,
Keys : [ ] string { } ,
} } ,
} ,
} ,
2022-04-13 13:46:49 +00:00
}
2022-04-21 11:41:50 +00:00
bearer . Object = append ( bearer . Object , getRestrictBearerRecords ( ) ... )
2022-04-13 13:46:49 +00:00
httpClient := defaultHTTPClient ( )
2022-08-19 06:44:58 +00:00
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearer } , httpClient , false )
2022-07-07 09:02:05 +00:00
bearerToken := bearerTokens [ 0 ]
2022-04-13 13:46:49 +00:00
2022-04-15 07:03:00 +00:00
query := make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
2022-07-12 08:01:21 +00:00
request , err := http . NewRequest ( http . MethodGet , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/" + objID . EncodeToString ( ) + "?" + query . Encode ( ) , nil )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 13:46:49 +00:00
prepareCommonHeaders ( request . Header , bearerToken )
objInfo := & models . ObjectInfo { }
doRequest ( t , httpClient , request , http . StatusOK , objInfo )
2022-04-11 09:35:06 +00:00
2022-07-12 08:01:21 +00:00
require . Equal ( t , cnrID . EncodeToString ( ) , * objInfo . ContainerID )
require . Equal ( t , objID . EncodeToString ( ) , * objInfo . ObjectID )
require . Equal ( t , ownerID . EncodeToString ( ) , * objInfo . OwnerID )
2022-04-13 13:46:49 +00:00
require . Equal ( t , len ( attributes ) , len ( objInfo . Attributes ) )
2022-04-20 14:10:43 +00:00
require . Equal ( t , int64 ( len ( content ) ) , * objInfo . ObjectSize )
contentData , err := base64 . StdEncoding . DecodeString ( objInfo . Payload )
require . NoError ( t , err )
require . Equal ( t , content , contentData )
2022-04-13 13:46:49 +00:00
for _ , attr := range objInfo . Attributes {
require . Equal ( t , attributes [ * attr . Key ] , * attr . Value )
}
2022-04-20 14:10:43 +00:00
// check max-payload-size params
query = make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
query . Add ( "max-payload-size" , "0" )
2022-07-12 08:01:21 +00:00
request , err = http . NewRequest ( http . MethodGet , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/" + objID . EncodeToString ( ) + "?" + query . Encode ( ) , nil )
2022-04-20 14:10:43 +00:00
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , bearerToken )
objInfo = & models . ObjectInfo { }
doRequest ( t , httpClient , request , http . StatusOK , objInfo )
require . Empty ( t , objInfo . Payload )
require . Equal ( t , int64 ( 0 ) , * objInfo . PayloadSize )
// check range params
rangeLength := 4
query = make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
query . Add ( "range-offset" , "0" )
query . Add ( "range-length" , strconv . Itoa ( rangeLength ) )
2022-07-12 08:01:21 +00:00
request , err = http . NewRequest ( http . MethodGet , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/" + objID . EncodeToString ( ) + "?" + query . Encode ( ) , nil )
2022-04-20 14:10:43 +00:00
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , bearerToken )
objInfo = & models . ObjectInfo { }
doRequest ( t , httpClient , request , http . StatusOK , objInfo )
require . Equal ( t , int64 ( rangeLength ) , * objInfo . PayloadSize )
contentData , err = base64 . StdEncoding . DecodeString ( objInfo . Payload )
require . NoError ( t , err )
require . Equal ( t , content [ : rangeLength ] , contentData )
2022-10-18 09:49:06 +00:00
// check empty object
objID2 := createObject ( ctx , t , p , ownerID , cnrID , map [ string ] string { } , [ ] byte { } )
query2 := make ( url . Values )
query2 . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
request2 , err := http . NewRequest ( http . MethodGet , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/" + objID2 . EncodeToString ( ) + "?" + query2 . Encode ( ) , nil )
require . NoError ( t , err )
prepareCommonHeaders ( request2 . Header , bearerToken )
objInfo2 := & models . ObjectInfo { }
doRequest ( t , httpClient , request2 , http . StatusOK , objInfo2 )
require . Equal ( t , cnrID . EncodeToString ( ) , * objInfo2 . ContainerID )
require . Equal ( t , objID2 . EncodeToString ( ) , * objInfo2 . ObjectID )
require . Equal ( t , ownerID . EncodeToString ( ) , * objInfo2 . OwnerID )
require . Equal ( t , 0 , len ( objInfo2 . Attributes ) )
require . Equal ( t , int64 ( 0 ) , * objInfo2 . ObjectSize )
2022-04-13 13:46:49 +00:00
}
2022-10-26 09:40:17 +00:00
func restObjectGetUnauthenticated ( 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-unauth-name" ,
"user-attribute" : "user value" ,
}
objID := createObject ( ctx , t , p , ownerID , cnrID , attributes , content )
httpClient := defaultHTTPClient ( )
request , err := http . NewRequest ( http . MethodGet , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/" + objID . EncodeToString ( ) , nil )
require . NoError ( t , err )
request . Header . Add ( "Content-Type" , "application/json" )
resp := & models . ErrorResponse { }
doRequest ( t , httpClient , request , http . StatusBadRequest , resp )
require . Equal ( t , int64 ( 2048 ) , resp . Code )
require . Equal ( t , models . ErrorTypeAPI , * resp . Type )
// set empty eacl table to be able to do unauthenticated request
allowByEACL ( ctx , t , p , cnrID )
request , err = http . NewRequest ( http . MethodGet , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/" + objID . EncodeToString ( ) , nil )
require . NoError ( t , err )
objInfo := & models . ObjectInfo { }
doRequest ( t , httpClient , request , http . StatusOK , objInfo )
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 )
contentData , err := base64 . StdEncoding . DecodeString ( objInfo . Payload )
require . NoError ( t , err )
require . Equal ( t , content , contentData )
for _ , attr := range objInfo . Attributes {
require . Equal ( t , attributes [ * attr . Key ] , * attr . Value )
}
// set eacl the same as was before test started
restrictByEACL ( ctx , t , p , cnrID )
}
2022-08-19 06:44:58 +00:00
func restObjectGetFullBearer ( 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 , ownerID , cnrID , attributes , content )
bearers := & models . Bearer {
Object : [ ] * models . Record {
{
Operation : models . NewOperation ( models . OperationHEAD ) ,
Action : models . NewAction ( models . ActionALLOW ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { {
Role : models . NewRole ( models . RoleOTHERS ) ,
Keys : [ ] string { } ,
} } ,
} ,
{
Operation : models . NewOperation ( models . OperationRANGE ) ,
Action : models . NewAction ( models . ActionALLOW ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { {
Role : models . NewRole ( models . RoleOTHERS ) ,
Keys : [ ] string { } ,
} } ,
} ,
} ,
}
bearers . Object = append ( bearers . Object , getRestrictBearerRecords ( ) ... )
httpClient := defaultHTTPClient ( )
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearers } , httpClient , true )
bearerToken := bearerTokens [ 0 ]
query := make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
request , err := http . NewRequest ( http . MethodGet , testHost + "/v1/auth/bearer?" + query . Encode ( ) , nil )
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , bearerToken )
resp := & models . BinaryBearer { }
doRequest ( t , httpClient , request , http . StatusOK , resp )
actualTokenRaw , err := base64 . StdEncoding . DecodeString ( * resp . Token )
require . NoError ( t , err )
var actualToken bearer . Token
err = actualToken . Unmarshal ( actualTokenRaw )
require . NoError ( t , err )
// check that is token for all users
require . True ( t , actualToken . AssertUser ( user . ID { } ) )
query . Add ( fullBearerQuery , "true" )
request , err = http . NewRequest ( http . MethodGet , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/" + objID . EncodeToString ( ) + "?" + query . Encode ( ) , nil )
require . NoError ( t , err )
request . Header . Add ( "Authorization" , "Bearer " + * resp . Token )
objInfo := & models . ObjectInfo { }
doRequest ( t , httpClient , request , http . StatusOK , objInfo )
contentData , err := base64 . StdEncoding . DecodeString ( objInfo . Payload )
require . NoError ( t , err )
require . Equal ( t , content , contentData )
}
2022-07-12 08:01:21 +00:00
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" ) )
2022-04-14 08:53:13 +00:00
bearer := & models . Bearer {
Object : [ ] * models . Record { {
Operation : models . NewOperation ( models . OperationDELETE ) ,
Action : models . NewAction ( models . ActionALLOW ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { {
Role : models . NewRole ( models . RoleOTHERS ) ,
Keys : [ ] string { } ,
} } ,
} } ,
}
2022-04-21 11:41:50 +00:00
bearer . Object = append ( bearer . Object , getRestrictBearerRecords ( ) ... )
2022-04-14 08:53:13 +00:00
httpClient := defaultHTTPClient ( )
2022-08-19 06:44:58 +00:00
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearer } , httpClient , false )
2022-07-07 09:02:05 +00:00
bearerToken := bearerTokens [ 0 ]
2022-04-14 08:53:13 +00:00
2022-04-15 07:03:00 +00:00
query := make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
2022-07-12 08:01:21 +00:00
request , err := http . NewRequest ( http . MethodDelete , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/" + objID . EncodeToString ( ) + "?" + query . Encode ( ) , nil )
2022-04-14 08:53:13 +00:00
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , bearerToken )
2022-07-07 09:29:51 +00:00
resp := & models . SuccessResponse { }
doRequest ( t , httpClient , request , http . StatusOK , resp )
require . True ( t , * resp . Success )
2022-04-14 08:53:13 +00:00
2022-07-12 08:01:21 +00:00
var addr oid . Address
addr . SetContainer ( cnrID )
addr . SetObject ( objID )
2022-04-14 08:53:13 +00:00
var prm pool . PrmObjectHead
prm . SetAddress ( addr )
_ , err = p . HeadObject ( ctx , prm )
require . Error ( t , err )
}
2022-07-12 08:01:21 +00:00
func restObjectsSearch ( ctx context . Context , t * testing . T , p * pool . Pool , owner * user . ID , cnrID cid . ID ) {
2022-04-18 08:30:34 +00:00
userKey , userValue := "User-Attribute" , "user-attribute-value"
objectName := "object-name"
2022-08-11 09:47:46 +00:00
filePath := "path/to/object/object-name"
2022-04-18 08:30:34 +00:00
headers := map [ string ] string {
object . AttributeFileName : objectName ,
2022-08-11 09:47:46 +00:00
"FilePath" : filePath ,
2022-04-18 08:30:34 +00:00
userKey : userValue ,
}
2022-07-12 08:01:21 +00:00
objID := createObject ( ctx , t , p , owner , cnrID , headers , [ ] byte ( "some content" ) )
2022-04-18 08:30:34 +00:00
headers [ userKey ] = "dummy"
2022-07-12 08:01:21 +00:00
_ = createObject ( ctx , t , p , owner , cnrID , headers , [ ] byte ( "some content" ) )
2022-04-18 08:30:34 +00:00
bearer := & models . Bearer {
Object : [ ] * models . Record {
{
Operation : models . NewOperation ( models . OperationSEARCH ) ,
Action : models . NewAction ( models . ActionALLOW ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { { Role : models . NewRole ( models . RoleOTHERS ) , Keys : [ ] string { } } } ,
} ,
{
Operation : models . NewOperation ( models . OperationHEAD ) ,
Action : models . NewAction ( models . ActionALLOW ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { { Role : models . NewRole ( models . RoleOTHERS ) , Keys : [ ] string { } } } ,
} ,
{
Operation : models . NewOperation ( models . OperationGET ) ,
Action : models . NewAction ( models . ActionALLOW ) ,
Filters : [ ] * models . Filter { } ,
Targets : [ ] * models . Target { { Role : models . NewRole ( models . RoleOTHERS ) , Keys : [ ] string { } } } ,
} ,
} ,
}
2022-04-21 11:41:50 +00:00
bearer . Object = append ( bearer . Object , getRestrictBearerRecords ( ) ... )
2022-04-18 08:30:34 +00:00
httpClient := defaultHTTPClient ( )
2022-08-19 06:44:58 +00:00
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearer } , httpClient , false )
2022-07-07 09:02:05 +00:00
bearerToken := bearerTokens [ 0 ]
2022-04-18 08:30:34 +00:00
search := & models . SearchFilters {
Filters : [ ] * models . SearchFilter {
{
2022-04-29 06:39:24 +00:00
Key : util . NewString ( userKey ) ,
2022-04-18 08:30:34 +00:00
Match : models . NewSearchMatch ( models . SearchMatchMatchStringEqual ) ,
2022-04-29 06:39:24 +00:00
Value : util . NewString ( userValue ) ,
2022-04-18 08:30:34 +00:00
} ,
} ,
}
body , err := json . Marshal ( search )
require . NoError ( t , err )
query := make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
2022-07-12 08:01:21 +00:00
request , err := http . NewRequest ( http . MethodPost , testHost + "/v1/objects/" + cnrID . EncodeToString ( ) + "/search?" + query . Encode ( ) , bytes . NewReader ( body ) )
2022-04-18 08:30:34 +00:00
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , bearerToken )
resp := & models . ObjectList { }
doRequest ( t , httpClient , request , http . StatusOK , resp )
require . Equal ( t , 1 , int ( * resp . Size ) )
require . Len ( t , resp . Objects , 1 )
objBaseInfo := resp . Objects [ 0 ]
2022-07-12 08:01:21 +00:00
require . Equal ( t , cnrID . EncodeToString ( ) , * objBaseInfo . Address . ContainerID )
require . Equal ( t , objID . EncodeToString ( ) , * objBaseInfo . Address . ObjectID )
2022-04-18 08:30:34 +00:00
require . Equal ( t , objectName , objBaseInfo . Name )
2022-08-11 09:47:46 +00:00
require . Equal ( t , filePath , objBaseInfo . FilePath )
2022-04-18 08:30:34 +00:00
}
2022-04-13 13:46:49 +00:00
func doRequest ( t * testing . T , httpClient * http . Client , request * http . Request , expectedCode int , model interface { } ) {
2022-04-11 09:35:06 +00:00
resp , err := httpClient . Do ( request )
require . NoError ( t , err )
2022-04-13 13:46:49 +00:00
defer func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} ( )
respBody , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
2022-04-11 09:35:06 +00:00
2022-04-13 13:46:49 +00:00
if expectedCode != resp . StatusCode {
fmt . Println ( "resp" , string ( respBody ) )
}
require . Equal ( t , expectedCode , resp . StatusCode )
if model == nil {
return
}
err = json . Unmarshal ( respBody , model )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 13:46:49 +00:00
}
2022-07-12 08:01:21 +00:00
func restContainerGet ( ctx context . Context , t * testing . T , owner user . ID , cnrID cid . ID ) {
2022-04-13 13:46:49 +00:00
httpClient := & http . Client { Timeout : 5 * time . Second }
2022-07-12 08:01:21 +00:00
request , err := http . NewRequest ( http . MethodGet , testHost + "/v1/containers/" + cnrID . EncodeToString ( ) , nil )
2022-04-13 13:46:49 +00:00
require . NoError ( t , err )
request = request . WithContext ( ctx )
cnrInfo := & models . ContainerInfo { }
doRequest ( t , httpClient , request , http . StatusOK , cnrInfo )
2022-04-11 09:35:06 +00:00
2022-07-12 08:01:21 +00:00
require . Equal ( t , cnrID . EncodeToString ( ) , * cnrInfo . ContainerID )
require . Equal ( t , owner . EncodeToString ( ) , * cnrInfo . OwnerID )
2022-07-19 14:48:28 +00:00
require . Equal ( t , containerName , * cnrInfo . ContainerName )
2022-07-21 15:04:11 +00:00
require . NotEmpty ( t , * cnrInfo . Version )
2022-04-11 09:35:06 +00:00
}
2022-07-12 08:01:21 +00:00
func restContainerDelete ( ctx context . Context , t * testing . T , clientPool * pool . Pool , owner user . ID ) {
cnrID := createContainer ( ctx , t , clientPool , owner , "for-delete" )
2022-04-13 08:41:04 +00:00
bearer := & models . Bearer {
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbDELETE ) ,
} ,
}
2022-04-13 13:46:49 +00:00
httpClient := defaultHTTPClient ( )
2022-08-19 06:44:58 +00:00
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearer } , httpClient , false )
2022-07-07 09:02:05 +00:00
bearerToken := bearerTokens [ 0 ]
2022-04-13 08:41:04 +00:00
2022-04-15 07:03:00 +00:00
query := make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
2022-07-12 08:01:21 +00:00
request , err := http . NewRequest ( http . MethodDelete , testHost + "/v1/containers/" + cnrID . EncodeToString ( ) + "?" + query . Encode ( ) , nil )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 08:41:04 +00:00
request = request . WithContext ( ctx )
2022-04-13 13:00:04 +00:00
prepareCommonHeaders ( request . Header , bearerToken )
2022-04-13 08:41:04 +00:00
2022-07-07 09:29:51 +00:00
resp := & models . SuccessResponse { }
doRequest ( t , httpClient , request , http . StatusOK , resp )
require . True ( t , * resp . Success )
2022-04-13 08:41:04 +00:00
var prm pool . PrmContainerGet
2022-07-12 08:01:21 +00:00
prm . SetContainerID ( cnrID )
2022-04-13 08:41:04 +00:00
_ , err = clientPool . GetContainer ( ctx , prm )
require . Error ( t , err )
require . Contains ( t , err . Error ( ) , "not found" )
2022-04-11 09:35:06 +00:00
}
2022-07-12 08:01:21 +00:00
func restContainerEACLPut ( ctx context . Context , t * testing . T , clientPool * pool . Pool , owner user . ID ) {
cnrID := createContainer ( ctx , t , clientPool , owner , "for-eacl-put" )
2022-04-13 13:00:04 +00:00
httpClient := & http . Client { Timeout : 60 * time . Second }
bearer := & models . Bearer {
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbSETEACL ) ,
} ,
}
2022-08-19 06:44:58 +00:00
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearer } , httpClient , false )
2022-07-07 09:02:05 +00:00
bearerToken := bearerTokens [ 0 ]
2022-04-13 13:00:04 +00:00
req := models . Eacl {
Records : [ ] * models . Record { {
Action : models . NewAction ( models . ActionDENY ) ,
Filters : [ ] * models . Filter { } ,
Operation : models . NewOperation ( models . OperationDELETE ) ,
Targets : [ ] * models . Target { {
2022-08-29 14:16:41 +00:00
Keys : [ ] string { "031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a" } ,
2022-04-13 13:00:04 +00:00
Role : models . NewRole ( models . RoleOTHERS ) ,
} } ,
} } ,
}
2022-08-29 14:16:41 +00:00
invalidBody , err := json . Marshal ( & req )
require . NoError ( t , err )
req . Records [ 0 ] . Targets [ 0 ] . Role = models . NewRole ( models . RoleKEYS )
2022-04-13 13:00:04 +00:00
body , err := json . Marshal ( & req )
require . NoError ( t , err )
2022-04-15 07:03:00 +00:00
query := make ( url . Values )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
2022-08-29 14:16:41 +00:00
doSetEACLRequest ( ctx , t , httpClient , cnrID , query , bearerToken , invalidBody , http . StatusBadRequest , nil )
2022-04-13 13:00:04 +00:00
2022-07-07 09:29:51 +00:00
resp := & models . SuccessResponse { }
2022-08-29 14:16:41 +00:00
doSetEACLRequest ( ctx , t , httpClient , cnrID , query , bearerToken , body , http . StatusOK , resp )
2022-07-07 09:29:51 +00:00
require . True ( t , * resp . Success )
2022-04-13 13:00:04 +00:00
var prm pool . PrmContainerEACL
2022-07-12 08:01:21 +00:00
prm . SetContainerID ( cnrID )
2022-04-13 13:00:04 +00:00
table , err := clientPool . GetEACL ( ctx , prm )
require . NoError ( t , err )
2022-04-29 06:39:24 +00:00
expectedTable , err := util . ToNativeTable ( req . Records )
2022-04-13 13:00:04 +00:00
require . NoError ( t , err )
expectedTable . SetCID ( cnrID )
2022-08-25 08:24:10 +00:00
require . True ( t , eacl . EqualTables ( * expectedTable , table ) )
2022-04-13 13:00:04 +00:00
}
2022-08-29 14:16:41 +00:00
func doSetEACLRequest ( ctx context . Context , t * testing . T , httpClient * http . Client , cnrID cid . ID , query url . Values , bearerToken * handlers . BearerToken , body [ ] byte , status int , model interface { } ) {
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 )
doRequest ( t , httpClient , request , status , model )
}
2022-07-12 08:01:21 +00:00
func restContainerEACLGet ( ctx context . Context , t * testing . T , p * pool . Pool , cnrID cid . ID ) {
2022-04-14 08:53:13 +00:00
var prm pool . PrmContainerEACL
2022-07-12 08:01:21 +00:00
prm . SetContainerID ( cnrID )
2022-04-14 08:53:13 +00:00
expectedTable , err := p . GetEACL ( ctx , prm )
require . NoError ( t , err )
2022-04-13 13:00:04 +00:00
httpClient := & http . Client { Timeout : 60 * time . Second }
2022-07-12 08:01:21 +00:00
request , err := http . NewRequest ( http . MethodGet , testHost + "/v1/containers/" + cnrID . EncodeToString ( ) + "/eacl" , nil )
2022-04-13 13:00:04 +00:00
require . NoError ( t , err )
request = request . WithContext ( ctx )
responseTable := & models . Eacl { }
2022-04-13 13:46:49 +00:00
doRequest ( t , httpClient , request , http . StatusOK , responseTable )
2022-04-13 13:00:04 +00:00
2022-07-12 08:01:21 +00:00
require . Equal ( t , cnrID . EncodeToString ( ) , responseTable . ContainerID )
2022-04-13 13:00:04 +00:00
2022-04-29 06:39:24 +00:00
actualTable , err := util . ToNativeTable ( responseTable . Records )
2022-04-13 13:00:04 +00:00
require . NoError ( t , err )
actualTable . SetCID ( cnrID )
2022-08-25 08:24:10 +00:00
require . True ( t , eacl . EqualTables ( expectedTable , * actualTable ) )
2022-04-13 13:00:04 +00:00
}
2022-07-12 08:01:21 +00:00
func restContainerList ( ctx context . Context , t * testing . T , p * pool . Pool , owner user . ID , cnrID cid . ID ) {
2022-04-13 15:23:03 +00:00
var prm pool . PrmContainerList
2022-07-12 08:01:21 +00:00
prm . SetOwnerID ( owner )
2022-04-13 15:23:03 +00:00
ids , err := p . ListContainers ( ctx , prm )
require . NoError ( t , err )
httpClient := defaultHTTPClient ( )
query := make ( url . Values )
2022-07-12 08:01:21 +00:00
query . Add ( "ownerId" , owner . EncodeToString ( ) )
2022-04-13 15:23:03 +00:00
request , err := http . NewRequest ( http . MethodGet , testHost + "/v1/containers?" + query . Encode ( ) , nil )
require . NoError ( t , err )
request = request . WithContext ( ctx )
list := & models . ContainerList { }
doRequest ( t , httpClient , request , http . StatusOK , list )
require . Equal ( t , len ( ids ) , int ( * list . Size ) )
2022-07-12 08:01:21 +00:00
require . Truef ( t , containsContainer ( list . Containers , cnrID . EncodeToString ( ) , containerName ) , "list doesn't contain cnr '%s' with name '%s'" , cnrID . EncodeToString ( ) , containerName )
2022-07-07 14:55:13 +00:00
}
func containsContainer ( containers [ ] * models . ContainerInfo , cnrID , cnrName string ) bool {
for _ , cnrInfo := range containers {
if * cnrInfo . ContainerID == cnrID {
for _ , attr := range cnrInfo . Attributes {
2022-07-12 08:01:21 +00:00
if * attr . Key == "Name" && * attr . Value == cnrName {
2022-07-07 14:55:13 +00:00
return true
}
}
fmt . Println ( "container found but name doesn't match" )
return false
}
2022-04-13 15:23:03 +00:00
}
2022-07-07 14:55:13 +00:00
return false
2022-04-13 15:23:03 +00:00
}
2022-08-19 06:44:58 +00:00
func makeAuthTokenRequest ( ctx context . Context , t * testing . T , bearers [ ] * models . Bearer , httpClient * http . Client , forAllUsers bool ) [ ] * handlers . BearerToken {
2022-04-11 09:35:06 +00:00
key , err := keys . NewPrivateKeyFromHex ( devenvPrivateKey )
require . NoError ( t , err )
2022-07-12 08:01:21 +00:00
var ownerID user . ID
user . IDFromKey ( & ownerID , key . PrivateKey . PublicKey )
2022-04-11 09:35:06 +00:00
2022-07-07 09:02:05 +00:00
data , err := json . Marshal ( bearers )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 08:41:04 +00:00
request , err := http . NewRequest ( http . MethodPost , testHost + "/v1/auth" , bytes . NewReader ( data ) )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 08:41:04 +00:00
request = request . WithContext ( ctx )
request . Header . Add ( "Content-Type" , "application/json" )
2022-06-09 09:59:02 +00:00
request . Header . Add ( XBearerOwnerID , ownerID . String ( ) )
2022-08-19 06:44:58 +00:00
request . Header . Add ( XBearerForAllUsers , strconv . FormatBool ( forAllUsers ) )
2022-04-11 09:35:06 +00:00
2022-04-13 08:41:04 +00:00
resp , err := httpClient . Do ( request )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 08:41:04 +00:00
defer func ( ) {
err := resp . Body . Close ( )
require . NoError ( t , err )
} ( )
2022-04-11 09:35:06 +00:00
rr , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
2022-04-13 08:41:04 +00:00
if resp . StatusCode != http . StatusOK {
fmt . Println ( "auth response" , string ( rr ) )
}
2022-04-11 09:35:06 +00:00
require . Equal ( t , http . StatusOK , resp . StatusCode )
2022-07-07 09:02:05 +00:00
var stokenResp [ ] * models . TokenResponse
err = json . Unmarshal ( rr , & stokenResp )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-07-07 09:02:05 +00:00
fmt . Println ( "resp tokens:" )
2022-04-11 09:35:06 +00:00
2022-07-07 09:02:05 +00:00
respTokens := make ( [ ] * handlers . BearerToken , len ( stokenResp ) )
for i , tok := range stokenResp {
isObject , err := handlers . IsObjectToken ( bearers [ i ] )
require . NoError ( t , err )
2022-04-11 09:35:06 +00:00
2022-07-07 09:02:05 +00:00
require . Equal ( t , bearers [ i ] . Name , tok . Name )
if isObject {
require . Equal ( t , models . TokenTypeObject , * tok . Type )
} else {
require . Equal ( t , models . TokenTypeContainer , * tok . Type )
}
binaryData , err := base64 . StdEncoding . DecodeString ( * tok . Token )
require . NoError ( t , err )
var bt * handlers . BearerToken
if useWalletConnect {
bt = signTokenWalletConnect ( t , key , binaryData )
} else {
bt = signToken ( t , key , binaryData )
}
respTokens [ i ] = bt
fmt . Printf ( "%+v\n" , bt )
2022-04-13 08:41:04 +00:00
}
2022-07-07 09:02:05 +00:00
return respTokens
2022-04-13 08:41:04 +00:00
}
2022-04-15 07:03:00 +00:00
func signToken ( t * testing . T , key * keys . PrivateKey , data [ ] byte ) * handlers . BearerToken {
2022-08-30 13:07:14 +00:00
signer := neofsecdsa . Signer ( key . PrivateKey )
sign , err := signer . Sign ( data )
2022-04-13 08:41:04 +00:00
require . NoError ( t , err )
2022-04-15 07:03:00 +00:00
return & handlers . BearerToken {
Token : base64 . StdEncoding . EncodeToString ( data ) ,
2022-06-16 09:12:26 +00:00
Signature : hex . EncodeToString ( sign ) ,
2022-04-15 07:03:00 +00:00
Key : hex . EncodeToString ( key . PublicKey ( ) . Bytes ( ) ) ,
}
}
func signTokenWalletConnect ( t * testing . T , key * keys . PrivateKey , data [ ] byte ) * handlers . BearerToken {
2022-07-12 08:01:21 +00:00
signer := neofsecdsa . SignerWalletConnect ( key . PrivateKey )
signature , err := signer . Sign ( data )
2022-04-15 07:03:00 +00:00
require . NoError ( t , err )
return & handlers . BearerToken {
2022-07-12 08:01:21 +00:00
Token : base64 . StdEncoding . EncodeToString ( data ) ,
Signature : hex . EncodeToString ( signature ) ,
2022-04-15 07:03:00 +00:00
Key : hex . EncodeToString ( key . PublicKey ( ) . Bytes ( ) ) ,
}
2022-04-13 08:41:04 +00:00
}
2022-07-07 13:33:36 +00:00
func restContainerPutInvalid ( ctx context . Context , t * testing . T ) {
bearer := & models . Bearer {
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbPUT ) ,
} ,
}
httpClient := & http . Client { Timeout : 30 * time . Second }
2022-08-19 06:44:58 +00:00
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearer } , httpClient , false )
2022-07-07 13:33:36 +00:00
bearerToken := bearerTokens [ 0 ]
reqURL , err := url . Parse ( testHost + "/v1/containers" )
require . NoError ( t , err )
query := reqURL . Query ( )
query . Add ( "name-scope-global" , "true" )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
reqURL . RawQuery = query . Encode ( )
2022-07-19 14:48:28 +00:00
body , err := json . Marshal ( & models . ContainerPutInfo { ContainerName : "nameWithCapitalLetters" } )
2022-07-07 13:33:36 +00:00
require . NoError ( t , err )
request , err := http . NewRequest ( http . MethodPut , reqURL . String ( ) , bytes . NewReader ( body ) )
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , bearerToken )
resp := & models . ErrorResponse { }
doRequest ( t , httpClient , request , http . StatusBadRequest , resp )
require . Equal ( t , int64 ( 0 ) , resp . Code )
require . Equal ( t , models . ErrorTypeGW , * resp . Type )
}
2022-04-13 08:41:04 +00:00
func restContainerPut ( ctx context . Context , t * testing . T , clientPool * pool . Pool ) {
bearer := & models . Bearer {
Container : & models . Rule {
Verb : models . NewVerb ( models . VerbPUT ) ,
} ,
}
httpClient := & http . Client { Timeout : 30 * time . Second }
2022-08-19 06:44:58 +00:00
bearerTokens := makeAuthTokenRequest ( ctx , t , [ ] * models . Bearer { bearer } , httpClient , false )
2022-07-07 09:02:05 +00:00
bearerToken := bearerTokens [ 0 ]
2022-04-11 09:35:06 +00:00
attrKey , attrValue := "User-Attribute" , "user value"
userAttributes := map [ string ] string {
attrKey : attrValue ,
}
2022-06-15 14:32:28 +00:00
// try to create container without name but with name-scope-global
2022-07-19 14:48:28 +00:00
body , err := json . Marshal ( & models . ContainerPutInfo { } )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 08:41:04 +00:00
reqURL , err := url . Parse ( testHost + "/v1/containers" )
require . NoError ( t , err )
query := reqURL . Query ( )
2022-06-15 14:32:28 +00:00
query . Add ( "name-scope-global" , "true" )
2022-04-15 07:03:00 +00:00
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
2022-04-13 08:41:04 +00:00
reqURL . RawQuery = query . Encode ( )
request , err := http . NewRequest ( http . MethodPut , reqURL . String ( ) , bytes . NewReader ( body ) )
2022-06-15 14:32:28 +00:00
require . NoError ( t , err )
prepareCommonHeaders ( request . Header , bearerToken )
doRequest ( t , httpClient , request , http . StatusBadRequest , nil )
// create container with name in local scope
2022-07-19 14:48:28 +00:00
containerPutInfo := & models . ContainerPutInfo {
Attributes : [ ] * models . Attribute { {
Key : util . NewString ( attrKey ) ,
Value : util . NewString ( attrValue ) ,
} } ,
}
body , err = json . Marshal ( containerPutInfo )
2022-06-15 14:32:28 +00:00
require . NoError ( t , err )
reqURL , err = url . Parse ( testHost + "/v1/containers" )
require . NoError ( t , err )
query = reqURL . Query ( )
query . Add ( walletConnectQuery , strconv . FormatBool ( useWalletConnect ) )
reqURL . RawQuery = query . Encode ( )
request , err = http . NewRequest ( http . MethodPut , reqURL . String ( ) , bytes . NewReader ( body ) )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-04-13 13:00:04 +00:00
prepareCommonHeaders ( request . Header , bearerToken )
2022-04-11 09:35:06 +00:00
addr := & operations . PutContainerOKBody { }
2022-04-13 13:46:49 +00:00
doRequest ( t , httpClient , request , http . StatusOK , addr )
2022-04-11 09:35:06 +00:00
var CID cid . ID
2022-07-12 08:01:21 +00:00
err = CID . DecodeString ( * addr . ContainerID )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
fmt . Println ( CID . String ( ) )
var prm pool . PrmContainerGet
prm . SetContainerID ( CID )
cnr , err := clientPool . GetContainer ( ctx , prm )
require . NoError ( t , err )
2022-07-12 08:01:21 +00:00
cnrAttr := make ( map [ string ] string )
cnr . IterateAttributes ( func ( key , val string ) {
cnrAttr [ key ] = val
} )
2022-04-11 09:35:06 +00:00
for key , val := range userAttributes {
require . Equal ( t , val , cnrAttr [ key ] )
}
}
2022-04-13 13:00:04 +00:00
func prepareCommonHeaders ( header http . Header , bearerToken * handlers . BearerToken ) {
header . Add ( "Content-Type" , "application/json" )
2022-04-14 08:53:13 +00:00
header . Add ( XBearerSignature , bearerToken . Signature )
2022-04-13 08:41:04 +00:00
header . Add ( "Authorization" , "Bearer " + bearerToken . Token )
2022-04-14 08:53:13 +00:00
header . Add ( XBearerSignatureKey , bearerToken . Key )
2022-04-13 08:41:04 +00:00
}
2022-07-12 08:01:21 +00:00
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" )
2022-04-11 09:35:06 +00:00
require . NoError ( t , err )
2022-07-12 08:01:21 +00:00
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 )
2022-04-11 09:35:06 +00:00
var waitPrm pool . WaitParams
waitPrm . SetPollInterval ( 3 * time . Second )
waitPrm . SetTimeout ( 15 * time . Second )
var prm pool . PrmContainerPut
2022-07-12 08:01:21 +00:00
prm . SetContainer ( cnr )
2022-04-11 09:35:06 +00:00
prm . SetWaitParams ( waitPrm )
CID , err := clientPool . PutContainer ( ctx , prm )
2022-04-13 08:41:04 +00:00
require . NoError ( t , err )
2022-04-11 09:35:06 +00:00
2022-08-25 08:24:10 +00:00
return CID
2022-04-11 09:35:06 +00:00
}
2022-07-12 08:01:21 +00:00
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 {
2022-04-13 13:46:49 +00:00
attributes := make ( [ ] object . Attribute , 0 , len ( headers ) )
for key , val := range headers {
attr := object . NewAttribute ( )
attr . SetKey ( key )
attr . SetValue ( val )
attributes = append ( attributes , * attr )
}
obj := object . New ( )
2022-07-12 08:01:21 +00:00
obj . SetOwnerID ( ownerID )
2022-04-13 13:46:49 +00:00
obj . SetContainerID ( cnrID )
obj . SetAttributes ( attributes ... )
obj . SetPayload ( payload )
var prm pool . PrmObjectPut
prm . SetHeader ( * obj )
objID , err := p . PutObject ( ctx , prm )
require . NoError ( t , err )
2022-08-25 08:24:10 +00:00
return objID
2022-04-13 13:46:49 +00:00
}
2022-07-12 08:01:21 +00:00
func restrictByEACL ( ctx context . Context , t * testing . T , clientPool * pool . Pool , cnrID cid . ID ) * eacl . Table {
2022-04-13 13:00:04 +00:00
table := eacl . NewTable ( )
2022-04-11 09:35:06 +00:00
table . SetCID ( cnrID )
for op := eacl . OperationGet ; op <= eacl . OperationRangeHash ; op ++ {
record := new ( eacl . Record )
record . SetOperation ( op )
record . SetAction ( eacl . ActionDeny )
target := new ( eacl . Target )
target . SetRole ( eacl . RoleOthers )
record . SetTargets ( * target )
table . AddRecord ( record )
}
var waitPrm pool . WaitParams
waitPrm . SetPollInterval ( 3 * time . Second )
waitPrm . SetTimeout ( 15 * time . Second )
var prm pool . PrmContainerSetEACL
prm . SetTable ( * table )
prm . SetWaitParams ( waitPrm )
err := clientPool . SetEACL ( ctx , prm )
require . NoError ( t , err )
2022-04-13 13:00:04 +00:00
return table
2022-04-11 09:35:06 +00:00
}
2022-10-26 09:40:17 +00:00
func allowByEACL ( ctx context . Context , t * testing . T , clientPool * pool . Pool , cnrID cid . ID ) * eacl . Table {
table := eacl . NewTable ( )
table . SetCID ( cnrID )
var waitPrm pool . WaitParams
waitPrm . SetPollInterval ( 3 * time . Second )
waitPrm . SetTimeout ( 15 * time . Second )
var prm pool . PrmContainerSetEACL
prm . SetTable ( * table )
prm . SetWaitParams ( waitPrm )
err := clientPool . SetEACL ( ctx , prm )
require . NoError ( t , err )
return table
}