From 33923a27c3dfd894c22c2ae674467251e0ffa8a1 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Wed, 13 Apr 2022 16:00:04 +0300 Subject: [PATCH] [#1] Add routes to manage container eacl Signed-off-by: Denis Kirillov --- cmd/neofs-rest-gw/integration_test.go | 126 ++++++++++- gen/models/eacl.go | 136 ++++++++++++ gen/restapi/embedded_spec.go | 204 +++++++++++++++++- gen/restapi/operations/get_container_e_acl.go | 56 +++++ .../get_container_e_acl_parameters.go | 71 ++++++ .../get_container_e_acl_responses.go | 100 +++++++++ gen/restapi/operations/neofs_rest_gw_api.go | 24 +++ gen/restapi/operations/put_container_e_acl.go | 71 ++++++ .../put_container_e_acl_parameters.go | 168 +++++++++++++++ .../put_container_e_acl_responses.go | 80 +++++++ handlers/api.go | 2 + handlers/containers.go | 83 +++++++ handlers/transformers.go | 160 +++++++++++++- spec/rest.yaml | 72 ++++++- 14 files changed, 1316 insertions(+), 37 deletions(-) create mode 100644 gen/models/eacl.go create mode 100644 gen/restapi/operations/get_container_e_acl.go create mode 100644 gen/restapi/operations/get_container_e_acl_parameters.go create mode 100644 gen/restapi/operations/get_container_e_acl_responses.go create mode 100644 gen/restapi/operations/put_container_e_acl.go create mode 100644 gen/restapi/operations/put_container_e_acl_parameters.go create mode 100644 gen/restapi/operations/put_container_e_acl_responses.go diff --git a/cmd/neofs-rest-gw/integration_test.go b/cmd/neofs-rest-gw/integration_test.go index 1a7d778..c1f2a9b 100644 --- a/cmd/neofs-rest-gw/integration_test.go +++ b/cmd/neofs-rest-gw/integration_test.go @@ -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 } diff --git a/gen/models/eacl.go b/gen/models/eacl.go new file mode 100644 index 0000000..c81d34b --- /dev/null +++ b/gen/models/eacl.go @@ -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 +} diff --git a/gen/restapi/embedded_spec.go b/gen/restapi/embedded_spec.go index 79df4df..ef23a16 100644 --- a/gen/restapi/embedded_spec.go +++ b/gen/restapi/embedded_spec.go @@ -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", diff --git a/gen/restapi/operations/get_container_e_acl.go b/gen/restapi/operations/get_container_e_acl.go new file mode 100644 index 0000000..84dcc23 --- /dev/null +++ b/gen/restapi/operations/get_container_e_acl.go @@ -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) + +} diff --git a/gen/restapi/operations/get_container_e_acl_parameters.go b/gen/restapi/operations/get_container_e_acl_parameters.go new file mode 100644 index 0000000..04a1366 --- /dev/null +++ b/gen/restapi/operations/get_container_e_acl_parameters.go @@ -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 +} diff --git a/gen/restapi/operations/get_container_e_acl_responses.go b/gen/restapi/operations/get_container_e_acl_responses.go new file mode 100644 index 0000000..061e340 --- /dev/null +++ b/gen/restapi/operations/get_container_e_acl_responses.go @@ -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 + } +} diff --git a/gen/restapi/operations/neofs_rest_gw_api.go b/gen/restapi/operations/neofs_rest_gw_api.go index d4ff810..54912c5 100644 --- a/gen/restapi/operations/neofs_rest_gw_api.go +++ b/gen/restapi/operations/neofs_rest_gw_api.go @@ -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) } diff --git a/gen/restapi/operations/put_container_e_acl.go b/gen/restapi/operations/put_container_e_acl.go new file mode 100644 index 0000000..085c765 --- /dev/null +++ b/gen/restapi/operations/put_container_e_acl.go @@ -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) + +} diff --git a/gen/restapi/operations/put_container_e_acl_parameters.go b/gen/restapi/operations/put_container_e_acl_parameters.go new file mode 100644 index 0000000..9ee77a5 --- /dev/null +++ b/gen/restapi/operations/put_container_e_acl_parameters.go @@ -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 +} diff --git a/gen/restapi/operations/put_container_e_acl_responses.go b/gen/restapi/operations/put_container_e_acl_responses.go new file mode 100644 index 0000000..7c9942d --- /dev/null +++ b/gen/restapi/operations/put_container_e_acl_responses.go @@ -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 + } +} diff --git a/handlers/api.go b/handlers/api.go index c6c11e7..8963b5e 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -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) { diff --git a/handlers/containers.go b/handlers/containers.go index e270112..52cc317 100644 --- a/handlers/containers.go +++ b/handlers/containers.go @@ -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 diff --git a/handlers/transformers.go b/handlers/transformers.go index a7dba95..294f0cf 100644 --- a/handlers/transformers.go +++ b/handlers/transformers.go @@ -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 } diff --git a/spec/rest.yaml b/spec/rest.yaml index 762929a..7d87094 100644 --- a/spec/rest.yaml +++ b/spec/rest.yaml @@ -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: