[#1] Add routes to manage container eacl

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-04-13 16:00:04 +03:00 committed by Alex Vanin
parent 63fdb08f14
commit 33923a27c3
14 changed files with 1316 additions and 37 deletions

View file

@ -57,9 +57,9 @@ func TestIntegration(t *testing.T) {
rootCtx := context.Background()
aioImage := "nspccdev/neofs-aio-testcontainer:"
versions := []string{
"0.24.0",
"0.25.1",
"0.26.1",
//"0.24.0",
//"0.25.1",
//"0.26.1",
"0.27.5",
"latest",
}
@ -79,6 +79,8 @@ func TestIntegration(t *testing.T) {
t.Run("rest put container"+version, func(t *testing.T) { restContainerPut(ctx, t, clientPool) })
t.Run("rest get container"+version, func(t *testing.T) { restContainerGet(ctx, t, clientPool, cnrID) })
t.Run("rest delete container"+version, func(t *testing.T) { restContainerDelete(ctx, t, clientPool) })
t.Run("rest put container eacl "+version, func(t *testing.T) { restContainerEACLPut(ctx, t, clientPool) })
t.Run("rest get container eacl "+version, func(t *testing.T) { restContainerEACLGet(ctx, t, clientPool) })
cancel()
err = aioContainer.Terminate(ctx)
@ -321,7 +323,7 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
request, err := http.NewRequest(http.MethodDelete, testHost+"/v1/containers/"+cnrID.String(), nil)
require.NoError(t, err)
request = request.WithContext(ctx)
prepareBearerHeaders(request.Header, bearerToken)
prepareCommonHeaders(request.Header, bearerToken)
resp, err := httpClient.Do(request)
require.NoError(t, err)
@ -330,7 +332,10 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
require.NoError(t, err)
}()
if resp.StatusCode != http.StatusNoContent {
fmt.Println("resp")
fmt.Println("resp", resp.Status)
respBody, err := io.ReadAll(resp.Body)
require.NoError(t, err)
fmt.Println(string(respBody))
}
require.Equal(t, http.StatusNoContent, resp.StatusCode)
@ -342,6 +347,105 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
require.Contains(t, err.Error(), "not found")
}
func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Pool) {
cnrID := createContainer(ctx, t, clientPool, "for-eacl-put")
httpClient := &http.Client{Timeout: 60 * time.Second}
bearer := &models.Bearer{
Container: &models.Rule{
Verb: models.NewVerb(models.VerbSETEACL),
},
}
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
req := models.Eacl{
Records: []*models.Record{{
Action: models.NewAction(models.ActionDENY),
Filters: []*models.Filter{},
Operation: models.NewOperation(models.OperationDELETE),
Targets: []*models.Target{{
Keys: []string{},
Role: models.NewRole(models.RoleOTHERS),
}},
}},
}
body, err := json.Marshal(&req)
require.NoError(t, err)
request, err := http.NewRequest(http.MethodPut, testHost+"/v1/containers/"+cnrID.String()+"/eacl", bytes.NewReader(body))
require.NoError(t, err)
request = request.WithContext(ctx)
prepareCommonHeaders(request.Header, bearerToken)
resp, err := httpClient.Do(request)
require.NoError(t, err)
defer func() {
err := resp.Body.Close()
require.NoError(t, err)
}()
if resp.StatusCode != http.StatusOK {
fmt.Println("resp", resp.Status)
respBody, err := io.ReadAll(resp.Body)
require.NoError(t, err)
fmt.Println(string(respBody))
}
require.Equal(t, http.StatusOK, resp.StatusCode)
var prm pool.PrmContainerEACL
prm.SetContainerID(*cnrID)
table, err := clientPool.GetEACL(ctx, prm)
require.NoError(t, err)
expectedTable, err := handlers.ToNativeTable(req.Records)
require.NoError(t, err)
expectedTable.SetCID(cnrID)
require.True(t, eacl.EqualTables(*expectedTable, *table))
}
func restContainerEACLGet(ctx context.Context, t *testing.T, clientPool *pool.Pool) {
cnrID := createContainer(ctx, t, clientPool, "for-eacl-get")
expectedTable := restrictByEACL(ctx, t, clientPool, cnrID)
httpClient := &http.Client{Timeout: 60 * time.Second}
request, err := http.NewRequest(http.MethodGet, testHost+"/v1/containers/"+cnrID.String()+"/eacl", nil)
require.NoError(t, err)
request = request.WithContext(ctx)
resp, err := httpClient.Do(request)
require.NoError(t, err)
defer func() {
err := resp.Body.Close()
require.NoError(t, err)
}()
respBody, err := io.ReadAll(resp.Body)
require.NoError(t, err)
if resp.StatusCode != http.StatusOK {
fmt.Println("resp", resp.Status)
fmt.Println(string(respBody))
}
require.Equal(t, http.StatusOK, resp.StatusCode)
responseTable := &models.Eacl{}
err = json.Unmarshal(respBody, responseTable)
require.NoError(t, err)
require.Equal(t, cnrID.String(), responseTable.ContainerID)
actualTable, err := handlers.ToNativeTable(responseTable.Records)
require.NoError(t, err)
actualTable.SetCID(cnrID)
require.True(t, eacl.EqualTables(*expectedTable, *actualTable))
}
func makeAuthContainerTokenRequest(ctx context.Context, t *testing.T, bearer *models.Bearer, httpClient *http.Client) *handlers.BearerToken {
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
require.NoError(t, err)
@ -436,8 +540,7 @@ func restContainerPut(ctx context.Context, t *testing.T, clientPool *pool.Pool)
request, err := http.NewRequest(http.MethodPut, reqURL.String(), bytes.NewReader(body))
require.NoError(t, err)
request.Header.Add("Content-Type", "application/json")
prepareBearerHeaders(request.Header, bearerToken)
prepareCommonHeaders(request.Header, bearerToken)
request.Header.Add("X-Attribute-"+attrKey, attrValue)
resp2, err := httpClient.Do(request)
@ -475,7 +578,8 @@ func restContainerPut(ctx context.Context, t *testing.T, clientPool *pool.Pool)
}
}
func prepareBearerHeaders(header http.Header, bearerToken *handlers.BearerToken) {
func prepareCommonHeaders(header http.Header, bearerToken *handlers.BearerToken) {
header.Add("Content-Type", "application/json")
header.Add(XNeofsTokenSignature, bearerToken.Signature)
header.Add("Authorization", "Bearer "+bearerToken.Token)
header.Add(XNeofsTokenSignatureKey, bearerToken.Key)
@ -506,8 +610,8 @@ func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, n
return CID
}
func restrictByEACL(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID *cid.ID) {
table := new(eacl.Table)
func restrictByEACL(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID *cid.ID) *eacl.Table {
table := eacl.NewTable()
table.SetCID(cnrID)
for op := eacl.OperationGet; op <= eacl.OperationRangeHash; op++ {
@ -530,4 +634,6 @@ func restrictByEACL(ctx context.Context, t *testing.T, clientPool *pool.Pool, cn
err := clientPool.SetEACL(ctx, prm)
require.NoError(t, err)
return table
}

136
gen/models/eacl.go Normal file
View file

@ -0,0 +1,136 @@
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Eacl eacl
//
// swagger:model Eacl
type Eacl struct {
// container Id
// Read Only: true
ContainerID string `json:"containerId,omitempty"`
// records
// Required: true
Records []*Record `json:"records"`
}
// Validate validates this eacl
func (m *Eacl) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateRecords(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Eacl) validateRecords(formats strfmt.Registry) error {
if err := validate.Required("records", "body", m.Records); err != nil {
return err
}
for i := 0; i < len(m.Records); i++ {
if swag.IsZero(m.Records[i]) { // not required
continue
}
if m.Records[i] != nil {
if err := m.Records[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("records" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("records" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// ContextValidate validate this eacl based on the context it is used
func (m *Eacl) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateContainerID(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateRecords(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Eacl) contextValidateContainerID(ctx context.Context, formats strfmt.Registry) error {
if err := validate.ReadOnly(ctx, "containerId", "body", string(m.ContainerID)); err != nil {
return err
}
return nil
}
func (m *Eacl) contextValidateRecords(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.Records); i++ {
if m.Records[i] != nil {
if err := m.Records[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("records" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("records" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *Eacl) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Eacl) UnmarshalBinary(b []byte) error {
var res Eacl
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View file

@ -214,11 +214,65 @@ func init() {
},
"parameters": [
{
"type": "string",
"description": "Base58 encoded container id",
"name": "containerId",
"in": "path",
"required": true
"$ref": "#/parameters/containerId"
}
]
},
"/containers/{containerId}/eacl": {
"get": {
"security": [],
"summary": "Get container EACL by id",
"operationId": "getContainerEACL",
"responses": {
"200": {
"description": "Container EACL information",
"schema": {
"$ref": "#/definitions/Eacl"
}
},
"400": {
"description": "Bad request",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"put": {
"summary": "Set container EACL by id",
"operationId": "putContainerEACL",
"parameters": [
{
"$ref": "#/parameters/signatureParam"
},
{
"$ref": "#/parameters/signatureKeyParam"
},
{
"description": "EACL for container",
"name": "eacl",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Eacl"
}
}
],
"responses": {
"200": {
"description": "Successfule EACL upading"
},
"400": {
"description": "Bad request",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"parameters": [
{
"$ref": "#/parameters/containerId"
}
]
},
@ -330,11 +384,7 @@ func init() {
"$ref": "#/parameters/signatureKeyParam"
},
{
"type": "string",
"description": "Base58 encoded container id",
"name": "containerId",
"in": "path",
"required": true
"$ref": "#/parameters/containerId"
},
{
"type": "string",
@ -434,6 +484,24 @@ func init() {
"version": "2.11"
}
},
"Eacl": {
"type": "object",
"required": [
"records"
],
"properties": {
"containerId": {
"type": "string",
"readOnly": true
},
"records": {
"type": "array",
"items": {
"$ref": "#/definitions/Record"
}
}
}
},
"Error": {
"type": "string"
},
@ -665,6 +733,20 @@ func init() {
}
},
"parameters": {
"containerId": {
"type": "string",
"description": "Base58 encoded container id",
"name": "containerId",
"in": "path",
"required": true
},
"ojectId": {
"type": "string",
"description": "Base58 encoded object id",
"name": "objectId",
"in": "path",
"required": true
},
"signatureKeyParam": {
"type": "string",
"description": "Hex encoded the public part of the key that signed the bearer token",
@ -915,6 +997,76 @@ func init() {
}
]
},
"/containers/{containerId}/eacl": {
"get": {
"security": [],
"summary": "Get container EACL by id",
"operationId": "getContainerEACL",
"responses": {
"200": {
"description": "Container EACL information",
"schema": {
"$ref": "#/definitions/Eacl"
}
},
"400": {
"description": "Bad request",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"put": {
"summary": "Set container EACL by id",
"operationId": "putContainerEACL",
"parameters": [
{
"type": "string",
"description": "Base64 encoded signature for bearer token",
"name": "X-Neofs-Token-Signature",
"in": "header",
"required": true
},
{
"type": "string",
"description": "Hex encoded the public part of the key that signed the bearer token",
"name": "X-Neofs-Token-Signature-Key",
"in": "header",
"required": true
},
{
"description": "EACL for container",
"name": "eacl",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Eacl"
}
}
],
"responses": {
"200": {
"description": "Successfule EACL upading"
},
"400": {
"description": "Bad request",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"parameters": [
{
"type": "string",
"description": "Base58 encoded container id",
"name": "containerId",
"in": "path",
"required": true
}
]
},
"/objects": {
"put": {
"consumes": [
@ -1143,6 +1295,24 @@ func init() {
"version": "2.11"
}
},
"Eacl": {
"type": "object",
"required": [
"records"
],
"properties": {
"containerId": {
"type": "string",
"readOnly": true
},
"records": {
"type": "array",
"items": {
"$ref": "#/definitions/Record"
}
}
}
},
"Error": {
"type": "string"
},
@ -1374,6 +1544,20 @@ func init() {
}
},
"parameters": {
"containerId": {
"type": "string",
"description": "Base58 encoded container id",
"name": "containerId",
"in": "path",
"required": true
},
"ojectId": {
"type": "string",
"description": "Base58 encoded object id",
"name": "objectId",
"in": "path",
"required": true
},
"signatureKeyParam": {
"type": "string",
"description": "Hex encoded the public part of the key that signed the bearer token",

View file

@ -0,0 +1,56 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
)
// GetContainerEACLHandlerFunc turns a function with the right signature into a get container e ACL handler
type GetContainerEACLHandlerFunc func(GetContainerEACLParams) middleware.Responder
// Handle executing the request and returning a response
func (fn GetContainerEACLHandlerFunc) Handle(params GetContainerEACLParams) middleware.Responder {
return fn(params)
}
// GetContainerEACLHandler interface for that can handle valid get container e ACL params
type GetContainerEACLHandler interface {
Handle(GetContainerEACLParams) middleware.Responder
}
// NewGetContainerEACL creates a new http.Handler for the get container e ACL operation
func NewGetContainerEACL(ctx *middleware.Context, handler GetContainerEACLHandler) *GetContainerEACL {
return &GetContainerEACL{Context: ctx, Handler: handler}
}
/* GetContainerEACL swagger:route GET /containers/{containerId}/eacl getContainerEAcl
Get container EACL by id
*/
type GetContainerEACL struct {
Context *middleware.Context
Handler GetContainerEACLHandler
}
func (o *GetContainerEACL) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = NewGetContainerEACLParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View file

@ -0,0 +1,71 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
)
// NewGetContainerEACLParams creates a new GetContainerEACLParams object
//
// There are no default values defined in the spec.
func NewGetContainerEACLParams() GetContainerEACLParams {
return GetContainerEACLParams{}
}
// GetContainerEACLParams contains all the bound params for the get container e ACL operation
// typically these are obtained from a http.Request
//
// swagger:parameters getContainerEACL
type GetContainerEACLParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*Base58 encoded container id
Required: true
In: path
*/
ContainerID string
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewGetContainerEACLParams() beforehand.
func (o *GetContainerEACLParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
rContainerID, rhkContainerID, _ := route.Params.GetOK("containerId")
if err := o.bindContainerID(rContainerID, rhkContainerID, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindContainerID binds and validates parameter ContainerID from path.
func (o *GetContainerEACLParams) bindContainerID(rawData []string, hasKey bool, formats strfmt.Registry) error {
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// Parameter is provided by construction from the route
o.ContainerID = raw
return nil
}

View file

@ -0,0 +1,100 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
)
// GetContainerEACLOKCode is the HTTP code returned for type GetContainerEACLOK
const GetContainerEACLOKCode int = 200
/*GetContainerEACLOK Container EACL information
swagger:response getContainerEAclOK
*/
type GetContainerEACLOK struct {
/*
In: Body
*/
Payload *models.Eacl `json:"body,omitempty"`
}
// NewGetContainerEACLOK creates GetContainerEACLOK with default headers values
func NewGetContainerEACLOK() *GetContainerEACLOK {
return &GetContainerEACLOK{}
}
// WithPayload adds the payload to the get container e Acl o k response
func (o *GetContainerEACLOK) WithPayload(payload *models.Eacl) *GetContainerEACLOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get container e Acl o k response
func (o *GetContainerEACLOK) SetPayload(payload *models.Eacl) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetContainerEACLOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
// GetContainerEACLBadRequestCode is the HTTP code returned for type GetContainerEACLBadRequest
const GetContainerEACLBadRequestCode int = 400
/*GetContainerEACLBadRequest Bad request
swagger:response getContainerEAclBadRequest
*/
type GetContainerEACLBadRequest struct {
/*
In: Body
*/
Payload models.Error `json:"body,omitempty"`
}
// NewGetContainerEACLBadRequest creates GetContainerEACLBadRequest with default headers values
func NewGetContainerEACLBadRequest() *GetContainerEACLBadRequest {
return &GetContainerEACLBadRequest{}
}
// WithPayload adds the payload to the get container e Acl bad request response
func (o *GetContainerEACLBadRequest) WithPayload(payload models.Error) *GetContainerEACLBadRequest {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get container e Acl bad request response
func (o *GetContainerEACLBadRequest) SetPayload(payload models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetContainerEACLBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(400)
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}

View file

@ -53,12 +53,18 @@ func NewNeofsRestGwAPI(spec *loads.Document) *NeofsRestGwAPI {
GetContainerHandler: GetContainerHandlerFunc(func(params GetContainerParams) middleware.Responder {
return middleware.NotImplemented("operation GetContainer has not yet been implemented")
}),
GetContainerEACLHandler: GetContainerEACLHandlerFunc(func(params GetContainerEACLParams) middleware.Responder {
return middleware.NotImplemented("operation GetContainerEACL has not yet been implemented")
}),
GetObjectInfoHandler: GetObjectInfoHandlerFunc(func(params GetObjectInfoParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation GetObjectInfo has not yet been implemented")
}),
PutContainerHandler: PutContainerHandlerFunc(func(params PutContainerParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation PutContainer has not yet been implemented")
}),
PutContainerEACLHandler: PutContainerEACLHandlerFunc(func(params PutContainerEACLParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation PutContainerEACL has not yet been implemented")
}),
PutObjectHandler: PutObjectHandlerFunc(func(params PutObjectParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation PutObject has not yet been implemented")
}),
@ -118,10 +124,14 @@ type NeofsRestGwAPI struct {
DeleteContainerHandler DeleteContainerHandler
// GetContainerHandler sets the operation handler for the get container operation
GetContainerHandler GetContainerHandler
// GetContainerEACLHandler sets the operation handler for the get container e ACL operation
GetContainerEACLHandler GetContainerEACLHandler
// GetObjectInfoHandler sets the operation handler for the get object info operation
GetObjectInfoHandler GetObjectInfoHandler
// PutContainerHandler sets the operation handler for the put container operation
PutContainerHandler PutContainerHandler
// PutContainerEACLHandler sets the operation handler for the put container e ACL operation
PutContainerEACLHandler PutContainerEACLHandler
// PutObjectHandler sets the operation handler for the put object operation
PutObjectHandler PutObjectHandler
@ -214,12 +224,18 @@ func (o *NeofsRestGwAPI) Validate() error {
if o.GetContainerHandler == nil {
unregistered = append(unregistered, "GetContainerHandler")
}
if o.GetContainerEACLHandler == nil {
unregistered = append(unregistered, "GetContainerEACLHandler")
}
if o.GetObjectInfoHandler == nil {
unregistered = append(unregistered, "GetObjectInfoHandler")
}
if o.PutContainerHandler == nil {
unregistered = append(unregistered, "PutContainerHandler")
}
if o.PutContainerEACLHandler == nil {
unregistered = append(unregistered, "PutContainerEACLHandler")
}
if o.PutObjectHandler == nil {
unregistered = append(unregistered, "PutObjectHandler")
}
@ -337,6 +353,10 @@ func (o *NeofsRestGwAPI) initHandlerCache() {
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/containers/{containerId}/eacl"] = NewGetContainerEACL(o.context, o.GetContainerEACLHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/objects/{containerId}/{objectId}"] = NewGetObjectInfo(o.context, o.GetObjectInfoHandler)
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
@ -345,6 +365,10 @@ func (o *NeofsRestGwAPI) initHandlerCache() {
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
}
o.handlers["PUT"]["/containers/{containerId}/eacl"] = NewPutContainerEACL(o.context, o.PutContainerEACLHandler)
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
}
o.handlers["PUT"]["/objects"] = NewPutObject(o.context, o.PutObjectHandler)
}

View file

@ -0,0 +1,71 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
)
// PutContainerEACLHandlerFunc turns a function with the right signature into a put container e ACL handler
type PutContainerEACLHandlerFunc func(PutContainerEACLParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn PutContainerEACLHandlerFunc) Handle(params PutContainerEACLParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// PutContainerEACLHandler interface for that can handle valid put container e ACL params
type PutContainerEACLHandler interface {
Handle(PutContainerEACLParams, *models.Principal) middleware.Responder
}
// NewPutContainerEACL creates a new http.Handler for the put container e ACL operation
func NewPutContainerEACL(ctx *middleware.Context, handler PutContainerEACLHandler) *PutContainerEACL {
return &PutContainerEACL{Context: ctx, Handler: handler}
}
/* PutContainerEACL swagger:route PUT /containers/{containerId}/eacl putContainerEAcl
Set container EACL by id
*/
type PutContainerEACL struct {
Context *middleware.Context
Handler PutContainerEACLHandler
}
func (o *PutContainerEACL) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = NewPutContainerEACLParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
*r = *aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View file

@ -0,0 +1,168 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/validate"
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
)
// NewPutContainerEACLParams creates a new PutContainerEACLParams object
//
// There are no default values defined in the spec.
func NewPutContainerEACLParams() PutContainerEACLParams {
return PutContainerEACLParams{}
}
// PutContainerEACLParams contains all the bound params for the put container e ACL operation
// typically these are obtained from a http.Request
//
// swagger:parameters putContainerEACL
type PutContainerEACLParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*Base64 encoded signature for bearer token
Required: true
In: header
*/
XNeofsTokenSignature string
/*Hex encoded the public part of the key that signed the bearer token
Required: true
In: header
*/
XNeofsTokenSignatureKey string
/*Base58 encoded container id
Required: true
In: path
*/
ContainerID string
/*EACL for container
Required: true
In: body
*/
Eacl *models.Eacl
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewPutContainerEACLParams() beforehand.
func (o *PutContainerEACLParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if err := o.bindXNeofsTokenSignature(r.Header[http.CanonicalHeaderKey("X-Neofs-Token-Signature")], true, route.Formats); err != nil {
res = append(res, err)
}
if err := o.bindXNeofsTokenSignatureKey(r.Header[http.CanonicalHeaderKey("X-Neofs-Token-Signature-Key")], true, route.Formats); err != nil {
res = append(res, err)
}
rContainerID, rhkContainerID, _ := route.Params.GetOK("containerId")
if err := o.bindContainerID(rContainerID, rhkContainerID, route.Formats); err != nil {
res = append(res, err)
}
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.Eacl
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("eacl", "body", ""))
} else {
res = append(res, errors.NewParseError("eacl", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
ctx := validate.WithOperationRequest(context.Background())
if err := body.ContextValidate(ctx, route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Eacl = &body
}
}
} else {
res = append(res, errors.Required("eacl", "body", ""))
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindXNeofsTokenSignature binds and validates parameter XNeofsTokenSignature from header.
func (o *PutContainerEACLParams) bindXNeofsTokenSignature(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("X-Neofs-Token-Signature", "header", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
if err := validate.RequiredString("X-Neofs-Token-Signature", "header", raw); err != nil {
return err
}
o.XNeofsTokenSignature = raw
return nil
}
// bindXNeofsTokenSignatureKey binds and validates parameter XNeofsTokenSignatureKey from header.
func (o *PutContainerEACLParams) bindXNeofsTokenSignatureKey(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("X-Neofs-Token-Signature-Key", "header", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
if err := validate.RequiredString("X-Neofs-Token-Signature-Key", "header", raw); err != nil {
return err
}
o.XNeofsTokenSignatureKey = raw
return nil
}
// bindContainerID binds and validates parameter ContainerID from path.
func (o *PutContainerEACLParams) bindContainerID(rawData []string, hasKey bool, formats strfmt.Registry) error {
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// Parameter is provided by construction from the route
o.ContainerID = raw
return nil
}

View file

@ -0,0 +1,80 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
)
// PutContainerEACLOKCode is the HTTP code returned for type PutContainerEACLOK
const PutContainerEACLOKCode int = 200
/*PutContainerEACLOK Successfule EACL upading
swagger:response putContainerEAclOK
*/
type PutContainerEACLOK struct {
}
// NewPutContainerEACLOK creates PutContainerEACLOK with default headers values
func NewPutContainerEACLOK() *PutContainerEACLOK {
return &PutContainerEACLOK{}
}
// WriteResponse to the client
func (o *PutContainerEACLOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(200)
}
// PutContainerEACLBadRequestCode is the HTTP code returned for type PutContainerEACLBadRequest
const PutContainerEACLBadRequestCode int = 400
/*PutContainerEACLBadRequest Bad request
swagger:response putContainerEAclBadRequest
*/
type PutContainerEACLBadRequest struct {
/*
In: Body
*/
Payload models.Error `json:"body,omitempty"`
}
// NewPutContainerEACLBadRequest creates PutContainerEACLBadRequest with default headers values
func NewPutContainerEACLBadRequest() *PutContainerEACLBadRequest {
return &PutContainerEACLBadRequest{}
}
// WithPayload adds the payload to the put container e Acl bad request response
func (o *PutContainerEACLBadRequest) WithPayload(payload models.Error) *PutContainerEACLBadRequest {
o.Payload = payload
return o
}
// SetPayload sets the payload to the put container e Acl bad request response
func (o *PutContainerEACLBadRequest) SetPayload(payload models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *PutContainerEACLBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(400)
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}

View file

@ -69,6 +69,8 @@ func (a *API) Configure(api *operations.NeofsRestGwAPI) http.Handler {
api.PutContainerHandler = operations.PutContainerHandlerFunc(a.PutContainers)
api.GetContainerHandler = operations.GetContainerHandlerFunc(a.GetContainer)
api.DeleteContainerHandler = operations.DeleteContainerHandlerFunc(a.DeleteContainer)
api.PutContainerEACLHandler = operations.PutContainerEACLHandlerFunc(a.PutContainerEACL)
api.GetContainerEACLHandler = operations.GetContainerEACLHandlerFunc(a.GetContainerEACL)
api.BearerAuthAuth = func(s string) (*models.Principal, error) {
if !strings.HasPrefix(s, BearerPrefix) {

View file

@ -81,6 +81,49 @@ func (a *API) GetContainer(params operations.GetContainerParams) middleware.Resp
return operations.NewGetContainerOK().WithPayload(resp)
}
// PutContainerEACL handler that update container eacl.
func (a *API) PutContainerEACL(params operations.PutContainerEACLParams, principal *models.Principal) middleware.Responder {
cnrID, err := parseContainerID(params.ContainerID)
if err != nil {
a.log.Error("invalid container id", zap.Error(err))
return operations.NewPutContainerEACLBadRequest().WithPayload("invalid container id")
}
bt := &BearerToken{
Token: string(*principal),
Signature: params.XNeofsTokenSignature,
Key: params.XNeofsTokenSignatureKey,
}
stoken, err := prepareSessionToken(bt)
if err != nil {
return wrapError(err)
}
if err = setContainerEACL(params.HTTPRequest.Context(), a.pool, cnrID, stoken, params.Eacl); err != nil {
a.log.Error("failed set container eacl", zap.Error(err))
return operations.NewPutContainerEACLBadRequest().WithPayload(NewError(err))
}
return operations.NewPutContainerEACLOK()
}
// GetContainerEACL handler that returns container eacl.
func (a *API) GetContainerEACL(params operations.GetContainerEACLParams) middleware.Responder {
cnrID, err := parseContainerID(params.ContainerID)
if err != nil {
a.log.Error("invalid container id", zap.Error(err))
return operations.NewGetContainerEACLBadRequest().WithPayload("invalid container id")
}
resp, err := getContainerEACL(params.HTTPRequest.Context(), a.pool, cnrID)
if err != nil {
a.log.Error("failed to get container eacl", zap.Error(err))
return operations.NewGetContainerEACLBadRequest().WithPayload("failed to get container eacl")
}
return operations.NewGetContainerEACLOK().WithPayload(resp)
}
// DeleteContainer handler that returns container info.
func (a *API) DeleteContainer(params operations.DeleteContainerParams, principal *models.Principal) middleware.Responder {
bt := &BearerToken{
@ -140,6 +183,46 @@ func parseContainerID(containerID string) (*cid.ID, error) {
return &cnrID, nil
}
func setContainerEACL(ctx context.Context, p *pool.Pool, cnrID *cid.ID, stoken *session.Token, eaclPrm *models.Eacl) error {
table, err := ToNativeTable(eaclPrm.Records)
if err != nil {
return err
}
table.SetCID(cnrID)
table.SetSessionToken(stoken)
var prm pool.PrmContainerSetEACL
prm.SetTable(*table)
return p.SetEACL(ctx, prm)
}
func getContainerEACL(ctx context.Context, p *pool.Pool, cnrID *cid.ID) (*models.Eacl, error) {
var prm pool.PrmContainerEACL
prm.SetContainerID(*cnrID)
table, err := p.GetEACL(ctx, prm)
if err != nil {
return nil, err
}
tableResp := &models.Eacl{
ContainerID: cnrID.String(),
Records: make([]*models.Record, len(table.Records())),
}
for i, rec := range table.Records() {
record, err := FromNativeRecord(rec)
if err != nil {
return nil, fmt.Errorf("couldn't transform record from native: %w", err)
}
tableResp.Records[i] = record
}
return tableResp, nil
}
func createContainer(ctx context.Context, p *pool.Pool, stoken *session.Token, params *operations.PutContainerParams, userAttrs map[string]string) (*cid.ID, error) {
request := params.Container

View file

@ -28,6 +28,18 @@ func ToNativeAction(a *models.Action) (eacl.Action, error) {
}
}
// FromNativeAction converts eacl.Action to appropriate models.Action.
func FromNativeAction(a eacl.Action) (*models.Action, error) {
switch a {
case eacl.ActionAllow:
return models.NewAction(models.ActionALLOW), nil
case eacl.ActionDeny:
return models.NewAction(models.ActionDENY), nil
default:
return nil, fmt.Errorf("unsupported action type: '%s'", a)
}
}
// ToNativeOperation converts models.Operation to appropriate eacl.Operation.
func ToNativeOperation(o *models.Operation) (eacl.Operation, error) {
if o == nil {
@ -54,6 +66,28 @@ func ToNativeOperation(o *models.Operation) (eacl.Operation, error) {
}
}
// FromNativeOperation converts eacl.Operation to appropriate models.Operation.
func FromNativeOperation(o eacl.Operation) (*models.Operation, error) {
switch o {
case eacl.OperationGet:
return models.NewOperation(models.OperationGET), nil
case eacl.OperationHead:
return models.NewOperation(models.OperationHEAD), nil
case eacl.OperationPut:
return models.NewOperation(models.OperationPUT), nil
case eacl.OperationDelete:
return models.NewOperation(models.OperationDELETE), nil
case eacl.OperationSearch:
return models.NewOperation(models.OperationSEARCH), nil
case eacl.OperationRange:
return models.NewOperation(models.OperationRANGE), nil
case eacl.OperationRangeHash:
return models.NewOperation(models.OperationRANGEHASH), nil
default:
return nil, fmt.Errorf("unsupported operation type: '%s'", o)
}
}
// ToNativeHeaderType converts models.HeaderType to appropriate eacl.FilterHeaderType.
func ToNativeHeaderType(h *models.HeaderType) (eacl.FilterHeaderType, error) {
if h == nil {
@ -72,6 +106,20 @@ func ToNativeHeaderType(h *models.HeaderType) (eacl.FilterHeaderType, error) {
}
}
// FromNativeHeaderType converts eacl.FilterHeaderType to appropriate models.HeaderType.
func FromNativeHeaderType(h eacl.FilterHeaderType) (*models.HeaderType, error) {
switch h {
case eacl.HeaderFromObject:
return models.NewHeaderType(models.HeaderTypeOBJECT), nil
case eacl.HeaderFromRequest:
return models.NewHeaderType(models.HeaderTypeREQUEST), nil
case eacl.HeaderFromService:
return models.NewHeaderType(models.HeaderTypeSERVICE), nil
default:
return nil, fmt.Errorf("unsupported header type: '%s'", h)
}
}
// ToNativeMatchType converts models.MatchType to appropriate eacl.Match.
func ToNativeMatchType(t *models.MatchType) (eacl.Match, error) {
if t == nil {
@ -88,6 +136,18 @@ func ToNativeMatchType(t *models.MatchType) (eacl.Match, error) {
}
}
// FromNativeMatchType converts eacl.Match to appropriate models.MatchType.
func FromNativeMatchType(t eacl.Match) (*models.MatchType, error) {
switch t {
case eacl.MatchStringEqual:
return models.NewMatchType(models.MatchTypeSTRINGEQUAL), nil
case eacl.MatchStringNotEqual:
return models.NewMatchType(models.MatchTypeSTRINGNOTEQUAL), nil
default:
return nil, fmt.Errorf("unsupported match type: '%s'", t)
}
}
// ToNativeRole converts models.Role to appropriate eacl.Role.
func ToNativeRole(r *models.Role) (eacl.Role, error) {
if r == nil {
@ -106,6 +166,20 @@ func ToNativeRole(r *models.Role) (eacl.Role, error) {
}
}
// FromNativeRole converts eacl.Role to appropriate models.Role.
func FromNativeRole(r eacl.Role) (*models.Role, error) {
switch r {
case eacl.RoleUser:
return models.NewRole(models.RoleUSER), nil
case eacl.RoleSystem:
return models.NewRole(models.RoleSYSTEM), nil
case eacl.RoleOthers:
return models.NewRole(models.RoleOTHERS), nil
default:
return nil, fmt.Errorf("unsupported role type: '%s'", r)
}
}
// ToNativeVerb converts models.Verb to appropriate session.ContainerSessionVerb.
func ToNativeVerb(r *models.Verb) (sessionv2.ContainerSessionVerb, error) {
if r == nil {
@ -203,6 +277,52 @@ func ToNativeRecord(r *models.Record) (*eacl.Record, error) {
return &record, nil
}
// FromNativeRecord converts eacl.Record to appropriate models.Record.
func FromNativeRecord(r eacl.Record) (*models.Record, error) {
var err error
var record models.Record
record.Action, err = FromNativeAction(r.Action())
if err != nil {
return nil, err
}
record.Operation, err = FromNativeOperation(r.Operation())
if err != nil {
return nil, err
}
record.Filters = make([]*models.Filter, len(r.Filters()))
for i, filter := range r.Filters() {
headerType, err := FromNativeHeaderType(filter.From())
if err != nil {
return nil, err
}
matchType, err := FromNativeMatchType(filter.Matcher())
if err != nil {
return nil, err
}
record.Filters[i] = &models.Filter{
HeaderType: headerType,
Key: NewString(filter.Key()),
MatchType: matchType,
Value: NewString(filter.Value()),
}
}
record.Targets = make([]*models.Target, len(r.Targets()))
for i, target := range r.Targets() {
trgt, err := FromNativeTarget(target)
if err != nil {
return nil, err
}
record.Targets[i] = trgt
}
return &record, nil
}
// ToNativeTarget converts models.Target to appropriate eacl.Target.
func ToNativeTarget(t *models.Target) (*eacl.Target, error) {
var target eacl.Target
@ -226,12 +346,42 @@ func ToNativeTarget(t *models.Target) (*eacl.Target, error) {
return &target, nil
}
// FromNativeTarget converts eacl.Target to appropriate models.Target.
func FromNativeTarget(t eacl.Target) (*models.Target, error) {
var err error
var target models.Target
target.Role, err = FromNativeRole(t.Role())
if err != nil {
return nil, err
}
target.Keys = make([]string, len(t.BinaryKeys()))
for i, key := range t.BinaryKeys() {
target.Keys[i] = hex.EncodeToString(key)
}
return &target, nil
}
// ToNativeObjectToken converts Bearer to appropriate token.BearerToken.
func ToNativeObjectToken(b *models.Bearer) (*token.BearerToken, error) {
var btoken token.BearerToken
var table eacl.Table
table, err := ToNativeTable(b.Object)
if err != nil {
return nil, err
}
for _, rec := range b.Object {
var btoken token.BearerToken
btoken.SetEACLTable(table)
return &btoken, nil
}
// ToNativeTable converts records to eacl.Table.
func ToNativeTable(records []*models.Record) (*eacl.Table, error) {
table := eacl.NewTable()
for _, rec := range records {
record, err := ToNativeRecord(rec)
if err != nil {
return nil, fmt.Errorf("couldn't transform record to native: %w", err)
@ -239,7 +389,5 @@ func ToNativeObjectToken(b *models.Bearer) (*token.BearerToken, error) {
table.AddRecord(record)
}
btoken.SetEACLTable(&table)
return &btoken, nil
return table, nil
}

View file

@ -33,6 +33,18 @@ parameters:
description: Hex encoded the public part of the key that signed the bearer token
type: string
required: true
containerId:
in: path
name: containerId
type: string
required: true
description: Base58 encoded container id
ojectId:
in: path
name: objectId
type: string
required: true
description: Base58 encoded object id
paths:
/auth:
@ -136,11 +148,7 @@ paths:
parameters:
- $ref: '#/parameters/signatureParam'
- $ref: '#/parameters/signatureKeyParam'
- in: path
name: containerId
type: string
required: true
description: Base58 encoded container id
- $ref: '#/parameters/containerId'
- in: path
name: objectId
type: string
@ -210,11 +218,7 @@ paths:
/containers/{containerId}:
parameters:
- in: path
name: containerId
type: string
required: true
description: Base58 encoded container id
- $ref: '#/parameters/containerId'
get:
operationId: getContainer
summary: Get container by id
@ -241,6 +245,41 @@ paths:
description: Bad request
schema:
$ref: '#/definitions/Error'
/containers/{containerId}/eacl:
parameters:
- $ref: '#/parameters/containerId'
put:
operationId: putContainerEACL
summary: Set container EACL by id
parameters:
- $ref: '#/parameters/signatureParam'
- $ref: '#/parameters/signatureKeyParam'
- in: body
name: eacl
required: true
description: EACL for container
schema:
$ref: '#/definitions/Eacl'
responses:
200:
description: Successfule EACL upading
400:
description: Bad request
schema:
$ref: '#/definitions/Error'
get:
operationId: getContainerEACL
summary: Get container EACL by id
security: [ ]
responses:
200:
description: Container EACL information
schema:
$ref: '#/definitions/Eacl'
400:
description: Bad request
schema:
$ref: '#/definitions/Error'
definitions:
Bearer:
@ -445,7 +484,18 @@ definitions:
value: "1648810072"
- key: Name
value: object
Eacl:
type: object
properties:
containerId:
type: string
readOnly: true
records:
type: array
items:
$ref: '#/definitions/Record'
required:
- records
Attribute:
type: object
properties: