From 066656ac486248c94d255a8ae872a7d1d976261d Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Mon, 11 Apr 2022 14:18:26 +0300 Subject: [PATCH] [#1] Add route to get object info Signed-off-by: Denis Kirillov --- gen/models/attribute.go | 39 ++- gen/models/container_info.go | 87 +++++- gen/models/object_info.go | 171 ++++++++++++ gen/restapi/embedded_spec.go | 250 ++++++++++++++++-- gen/restapi/operations/get_object_info.go | 71 +++++ .../operations/get_object_info_parameters.go | 154 +++++++++++ .../operations/get_object_info_responses.go | 100 +++++++ gen/restapi/operations/neofs_rest_gw_api.go | 12 + .../operations/put_container_parameters.go | 6 +- handlers/api.go | 8 +- handlers/containers.go | 15 +- handlers/objects.go | 71 ++++- handlers/util.go | 6 + spec/rest.yaml | 112 ++++++-- 14 files changed, 1032 insertions(+), 70 deletions(-) create mode 100644 gen/models/object_info.go create mode 100644 gen/restapi/operations/get_object_info.go create mode 100644 gen/restapi/operations/get_object_info_parameters.go create mode 100644 gen/restapi/operations/get_object_info_responses.go diff --git a/gen/models/attribute.go b/gen/models/attribute.go index 8b2a10a..bfec64d 100644 --- a/gen/models/attribute.go +++ b/gen/models/attribute.go @@ -8,8 +8,10 @@ package models import ( "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" + "github.com/go-openapi/validate" ) // Attribute attribute @@ -18,14 +20,47 @@ import ( type Attribute struct { // key - Key string `json:"key,omitempty"` + // Required: true + Key *string `json:"key"` // value - Value string `json:"value,omitempty"` + // Required: true + Value *string `json:"value"` } // Validate validates this attribute func (m *Attribute) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateKey(formats); err != nil { + res = append(res, err) + } + + if err := m.validateValue(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Attribute) validateKey(formats strfmt.Registry) error { + + if err := validate.Required("key", "body", m.Key); err != nil { + return err + } + + return nil +} + +func (m *Attribute) validateValue(formats strfmt.Registry) error { + + if err := validate.Required("value", "body", m.Value); err != nil { + return err + } + return nil } diff --git a/gen/models/container_info.go b/gen/models/container_info.go index aa4da3a..21ca821 100644 --- a/gen/models/container_info.go +++ b/gen/models/container_info.go @@ -12,6 +12,7 @@ import ( "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" + "github.com/go-openapi/validate" ) // ContainerInfo container info @@ -21,22 +22,28 @@ import ( type ContainerInfo struct { // attributes + // Required: true Attributes []*Attribute `json:"attributes"` // basic Acl - BasicACL string `json:"basicAcl,omitempty"` + // Required: true + BasicACL *string `json:"basicAcl"` // container Id - ContainerID string `json:"containerId,omitempty"` + // Required: true + ContainerID *string `json:"containerId"` // owner Id - OwnerID string `json:"ownerId,omitempty"` + // Required: true + OwnerID *string `json:"ownerId"` // placement policy - PlacementPolicy string `json:"placementPolicy,omitempty"` + // Required: true + PlacementPolicy *string `json:"placementPolicy"` // version - Version string `json:"version,omitempty"` + // Required: true + Version *string `json:"version"` } // Validate validates this container info @@ -47,6 +54,26 @@ func (m *ContainerInfo) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateBasicACL(formats); err != nil { + res = append(res, err) + } + + if err := m.validateContainerID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateOwnerID(formats); err != nil { + res = append(res, err) + } + + if err := m.validatePlacementPolicy(formats); err != nil { + res = append(res, err) + } + + if err := m.validateVersion(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -54,8 +81,9 @@ func (m *ContainerInfo) Validate(formats strfmt.Registry) error { } func (m *ContainerInfo) validateAttributes(formats strfmt.Registry) error { - if swag.IsZero(m.Attributes) { // not required - return nil + + if err := validate.Required("attributes", "body", m.Attributes); err != nil { + return err } for i := 0; i < len(m.Attributes); i++ { @@ -79,6 +107,51 @@ func (m *ContainerInfo) validateAttributes(formats strfmt.Registry) error { return nil } +func (m *ContainerInfo) validateBasicACL(formats strfmt.Registry) error { + + if err := validate.Required("basicAcl", "body", m.BasicACL); err != nil { + return err + } + + return nil +} + +func (m *ContainerInfo) validateContainerID(formats strfmt.Registry) error { + + if err := validate.Required("containerId", "body", m.ContainerID); err != nil { + return err + } + + return nil +} + +func (m *ContainerInfo) validateOwnerID(formats strfmt.Registry) error { + + if err := validate.Required("ownerId", "body", m.OwnerID); err != nil { + return err + } + + return nil +} + +func (m *ContainerInfo) validatePlacementPolicy(formats strfmt.Registry) error { + + if err := validate.Required("placementPolicy", "body", m.PlacementPolicy); err != nil { + return err + } + + return nil +} + +func (m *ContainerInfo) validateVersion(formats strfmt.Registry) error { + + if err := validate.Required("version", "body", m.Version); err != nil { + return err + } + + return nil +} + // ContextValidate validate this container info based on the context it is used func (m *ContainerInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error diff --git a/gen/models/object_info.go b/gen/models/object_info.go new file mode 100644 index 0000000..db5f14b --- /dev/null +++ b/gen/models/object_info.go @@ -0,0 +1,171 @@ +// 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" +) + +// ObjectInfo object info +// Example: {"attribute":[{"key":"Timestamp","value":"1648810072"},{"key":"Name","value":"object"}],"containerId":"5HZTn5qkRnmgSz9gSrw22CEdPPk6nQhkwf2Mgzyvkikv","objectId":"8N3o7Dtr6T1xteCt6eRwhpmJ7JhME58Hyu1dvaswuTDd","ownerId":"NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM"} +// +// swagger:model ObjectInfo +type ObjectInfo struct { + + // attributes + // Required: true + Attributes []*Attribute `json:"attributes"` + + // container Id + // Required: true + ContainerID *string `json:"containerId"` + + // object Id + // Required: true + ObjectID *string `json:"objectId"` + + // owner Id + // Required: true + OwnerID *string `json:"ownerId"` +} + +// Validate validates this object info +func (m *ObjectInfo) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateAttributes(formats); err != nil { + res = append(res, err) + } + + if err := m.validateContainerID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateObjectID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateOwnerID(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ObjectInfo) validateAttributes(formats strfmt.Registry) error { + + if err := validate.Required("attributes", "body", m.Attributes); err != nil { + return err + } + + for i := 0; i < len(m.Attributes); i++ { + if swag.IsZero(m.Attributes[i]) { // not required + continue + } + + if m.Attributes[i] != nil { + if err := m.Attributes[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("attributes" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("attributes" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *ObjectInfo) validateContainerID(formats strfmt.Registry) error { + + if err := validate.Required("containerId", "body", m.ContainerID); err != nil { + return err + } + + return nil +} + +func (m *ObjectInfo) validateObjectID(formats strfmt.Registry) error { + + if err := validate.Required("objectId", "body", m.ObjectID); err != nil { + return err + } + + return nil +} + +func (m *ObjectInfo) validateOwnerID(formats strfmt.Registry) error { + + if err := validate.Required("ownerId", "body", m.OwnerID); err != nil { + return err + } + + return nil +} + +// ContextValidate validate this object info based on the context it is used +func (m *ObjectInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateAttributes(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ObjectInfo) contextValidateAttributes(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Attributes); i++ { + + if m.Attributes[i] != nil { + if err := m.Attributes[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("attributes" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("attributes" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ObjectInfo) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ObjectInfo) UnmarshalBinary(b []byte) error { + var res ObjectInfo + 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 63b21ba..a60e76f 100644 --- a/gen/restapi/embedded_spec.go +++ b/gen/restapi/embedded_spec.go @@ -155,18 +155,10 @@ func init() { }, "parameters": [ { - "type": "string", - "description": "Base64 encoded signature for bearer token", - "name": "X-Neofs-Token-Signature", - "in": "header", - "required": true + "$ref": "#/parameters/signatureParam" }, { - "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 + "$ref": "#/parameters/signatureKeyParam" } ] }, @@ -273,18 +265,52 @@ func init() { } }, "parameters": [ + { + "$ref": "#/parameters/signatureParam" + }, + { + "$ref": "#/parameters/signatureKeyParam" + } + ] + }, + "/objects/{containerId}/{objectId}": { + "get": { + "summary": "Get object info by address and", + "operationId": "getObjectInfo", + "responses": { + "200": { + "description": "Object info", + "schema": { + "$ref": "#/definitions/ObjectInfo" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "parameters": [ + { + "$ref": "#/parameters/signatureParam" + }, + { + "$ref": "#/parameters/signatureKeyParam" + }, { "type": "string", - "description": "Base64 encoded signature for bearer token", - "name": "X-Neofs-Token-Signature", - "in": "header", + "description": "Base58 encoded container id", + "name": "containerId", + "in": "path", "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", + "description": "Base58 encoded object id", + "name": "objectId", + "in": "path", "required": true } ] @@ -300,6 +326,10 @@ func init() { }, "Attribute": { "type": "object", + "required": [ + "key", + "value" + ], "properties": { "key": { "type": "string" @@ -325,6 +355,14 @@ func init() { }, "ContainerInfo": { "type": "object", + "required": [ + "containerId", + "version", + "ownerId", + "basicAcl", + "placementPolicy", + "attributes" + ], "properties": { "attributes": { "type": "array", @@ -413,6 +451,47 @@ func init() { "STRING_NOT_EQUAL" ] }, + "ObjectInfo": { + "type": "object", + "required": [ + "containerId", + "objectId", + "ownerId", + "attributes" + ], + "properties": { + "attributes": { + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + } + }, + "containerId": { + "type": "string" + }, + "objectId": { + "type": "string" + }, + "ownerId": { + "type": "string" + } + }, + "example": { + "attribute": [ + { + "key": "Timestamp", + "value": "1648810072" + }, + { + "key": "Name", + "value": "object" + } + ], + "containerId": "5HZTn5qkRnmgSz9gSrw22CEdPPk6nQhkwf2Mgzyvkikv", + "objectId": "8N3o7Dtr6T1xteCt6eRwhpmJ7JhME58Hyu1dvaswuTDd", + "ownerId": "NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM" + } + }, "Operation": { "type": "string", "enum": [ @@ -555,8 +634,25 @@ func init() { ] } }, + "parameters": { + "signatureKeyParam": { + "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 + }, + "signatureParam": { + "type": "string", + "description": "Base64 encoded signature for bearer token", + "name": "X-Neofs-Token-Signature", + "in": "header", + "required": true + } + }, "securityDefinitions": { "BearerAuth": { + "description": "Bearer token body to provide with NeoFS request. Must have 'Bearer ' prefix.", "type": "apiKey", "name": "Authorization", "in": "header" @@ -715,7 +811,7 @@ func init() { { "type": "string", "description": "Hex encoded the public part of the key that signed the bearer token", - "name": "X-Neofs-Token-signature-Key", + "name": "X-Neofs-Token-Signature-Key", "in": "header", "required": true } @@ -839,6 +935,56 @@ func init() { "required": true } ] + }, + "/objects/{containerId}/{objectId}": { + "get": { + "summary": "Get object info by address and", + "operationId": "getObjectInfo", + "responses": { + "200": { + "description": "Object info", + "schema": { + "$ref": "#/definitions/ObjectInfo" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "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 + }, + { + "type": "string", + "description": "Base58 encoded container id", + "name": "containerId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Base58 encoded object id", + "name": "objectId", + "in": "path", + "required": true + } + ] } }, "definitions": { @@ -851,6 +997,10 @@ func init() { }, "Attribute": { "type": "object", + "required": [ + "key", + "value" + ], "properties": { "key": { "type": "string" @@ -876,6 +1026,14 @@ func init() { }, "ContainerInfo": { "type": "object", + "required": [ + "containerId", + "version", + "ownerId", + "basicAcl", + "placementPolicy", + "attributes" + ], "properties": { "attributes": { "type": "array", @@ -964,6 +1122,47 @@ func init() { "STRING_NOT_EQUAL" ] }, + "ObjectInfo": { + "type": "object", + "required": [ + "containerId", + "objectId", + "ownerId", + "attributes" + ], + "properties": { + "attributes": { + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + } + }, + "containerId": { + "type": "string" + }, + "objectId": { + "type": "string" + }, + "ownerId": { + "type": "string" + } + }, + "example": { + "attribute": [ + { + "key": "Timestamp", + "value": "1648810072" + }, + { + "key": "Name", + "value": "object" + } + ], + "containerId": "5HZTn5qkRnmgSz9gSrw22CEdPPk6nQhkwf2Mgzyvkikv", + "objectId": "8N3o7Dtr6T1xteCt6eRwhpmJ7JhME58Hyu1dvaswuTDd", + "ownerId": "NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM" + } + }, "Operation": { "type": "string", "enum": [ @@ -1106,8 +1305,25 @@ func init() { ] } }, + "parameters": { + "signatureKeyParam": { + "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 + }, + "signatureParam": { + "type": "string", + "description": "Base64 encoded signature for bearer token", + "name": "X-Neofs-Token-Signature", + "in": "header", + "required": true + } + }, "securityDefinitions": { "BearerAuth": { + "description": "Bearer token body to provide with NeoFS request. Must have 'Bearer ' prefix.", "type": "apiKey", "name": "Authorization", "in": "header" diff --git a/gen/restapi/operations/get_object_info.go b/gen/restapi/operations/get_object_info.go new file mode 100644 index 0000000..b617801 --- /dev/null +++ b/gen/restapi/operations/get_object_info.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" +) + +// GetObjectInfoHandlerFunc turns a function with the right signature into a get object info handler +type GetObjectInfoHandlerFunc func(GetObjectInfoParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetObjectInfoHandlerFunc) Handle(params GetObjectInfoParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetObjectInfoHandler interface for that can handle valid get object info params +type GetObjectInfoHandler interface { + Handle(GetObjectInfoParams, *models.Principal) middleware.Responder +} + +// NewGetObjectInfo creates a new http.Handler for the get object info operation +func NewGetObjectInfo(ctx *middleware.Context, handler GetObjectInfoHandler) *GetObjectInfo { + return &GetObjectInfo{Context: ctx, Handler: handler} +} + +/* GetObjectInfo swagger:route GET /objects/{containerId}/{objectId} getObjectInfo + +Get object info by address and + +*/ +type GetObjectInfo struct { + Context *middleware.Context + Handler GetObjectInfoHandler +} + +func (o *GetObjectInfo) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetObjectInfoParams() + 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/get_object_info_parameters.go b/gen/restapi/operations/get_object_info_parameters.go new file mode 100644 index 0000000..2aba4c1 --- /dev/null +++ b/gen/restapi/operations/get_object_info_parameters.go @@ -0,0 +1,154 @@ +// 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" + "github.com/go-openapi/validate" +) + +// NewGetObjectInfoParams creates a new GetObjectInfoParams object +// +// There are no default values defined in the spec. +func NewGetObjectInfoParams() GetObjectInfoParams { + + return GetObjectInfoParams{} +} + +// GetObjectInfoParams contains all the bound params for the get object info operation +// typically these are obtained from a http.Request +// +// swagger:parameters getObjectInfo +type GetObjectInfoParams 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 + /*Base58 encoded object id + Required: true + In: path + */ + ObjectID 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 NewGetObjectInfoParams() beforehand. +func (o *GetObjectInfoParams) 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) + } + + rObjectID, rhkObjectID, _ := route.Params.GetOK("objectId") + if err := o.bindObjectID(rObjectID, rhkObjectID, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindXNeofsTokenSignature binds and validates parameter XNeofsTokenSignature from header. +func (o *GetObjectInfoParams) 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 *GetObjectInfoParams) 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 *GetObjectInfoParams) 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 +} + +// bindObjectID binds and validates parameter ObjectID from path. +func (o *GetObjectInfoParams) bindObjectID(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.ObjectID = raw + + return nil +} diff --git a/gen/restapi/operations/get_object_info_responses.go b/gen/restapi/operations/get_object_info_responses.go new file mode 100644 index 0000000..0d18347 --- /dev/null +++ b/gen/restapi/operations/get_object_info_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" +) + +// GetObjectInfoOKCode is the HTTP code returned for type GetObjectInfoOK +const GetObjectInfoOKCode int = 200 + +/*GetObjectInfoOK Object info + +swagger:response getObjectInfoOK +*/ +type GetObjectInfoOK struct { + + /* + In: Body + */ + Payload *models.ObjectInfo `json:"body,omitempty"` +} + +// NewGetObjectInfoOK creates GetObjectInfoOK with default headers values +func NewGetObjectInfoOK() *GetObjectInfoOK { + + return &GetObjectInfoOK{} +} + +// WithPayload adds the payload to the get object info o k response +func (o *GetObjectInfoOK) WithPayload(payload *models.ObjectInfo) *GetObjectInfoOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get object info o k response +func (o *GetObjectInfoOK) SetPayload(payload *models.ObjectInfo) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetObjectInfoOK) 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 + } + } +} + +// GetObjectInfoBadRequestCode is the HTTP code returned for type GetObjectInfoBadRequest +const GetObjectInfoBadRequestCode int = 400 + +/*GetObjectInfoBadRequest Bad request + +swagger:response getObjectInfoBadRequest +*/ +type GetObjectInfoBadRequest struct { + + /* + In: Body + */ + Payload models.Error `json:"body,omitempty"` +} + +// NewGetObjectInfoBadRequest creates GetObjectInfoBadRequest with default headers values +func NewGetObjectInfoBadRequest() *GetObjectInfoBadRequest { + + return &GetObjectInfoBadRequest{} +} + +// WithPayload adds the payload to the get object info bad request response +func (o *GetObjectInfoBadRequest) WithPayload(payload models.Error) *GetObjectInfoBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get object info bad request response +func (o *GetObjectInfoBadRequest) SetPayload(payload models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetObjectInfoBadRequest) 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 8c49286..bd9d25d 100644 --- a/gen/restapi/operations/neofs_rest_gw_api.go +++ b/gen/restapi/operations/neofs_rest_gw_api.go @@ -50,6 +50,9 @@ func NewNeofsRestGwAPI(spec *loads.Document) *NeofsRestGwAPI { GetContainerHandler: GetContainerHandlerFunc(func(params GetContainerParams) middleware.Responder { return middleware.NotImplemented("operation GetContainer 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") }), @@ -110,6 +113,8 @@ type NeofsRestGwAPI struct { AuthHandler AuthHandler // GetContainerHandler sets the operation handler for the get container operation GetContainerHandler GetContainerHandler + // 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 // PutObjectHandler sets the operation handler for the put object operation @@ -201,6 +206,9 @@ func (o *NeofsRestGwAPI) Validate() error { if o.GetContainerHandler == nil { unregistered = append(unregistered, "GetContainerHandler") } + if o.GetObjectInfoHandler == nil { + unregistered = append(unregistered, "GetObjectInfoHandler") + } if o.PutContainerHandler == nil { unregistered = append(unregistered, "PutContainerHandler") } @@ -314,6 +322,10 @@ func (o *NeofsRestGwAPI) initHandlerCache() { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/containers/{containerId}"] = NewGetContainer(o.context, o.GetContainerHandler) + 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) } diff --git a/gen/restapi/operations/put_container_parameters.go b/gen/restapi/operations/put_container_parameters.go index 7b807a8..461d1f9 100644 --- a/gen/restapi/operations/put_container_parameters.go +++ b/gen/restapi/operations/put_container_parameters.go @@ -64,7 +64,7 @@ func (o *PutContainerParams) BindRequest(r *http.Request, route *middleware.Matc res = append(res, err) } - if err := o.bindXNeofsTokenSignatureKey(r.Header[http.CanonicalHeaderKey("X-Neofs-Token-signature-Key")], true, route.Formats); err != nil { + if err := o.bindXNeofsTokenSignatureKey(r.Header[http.CanonicalHeaderKey("X-Neofs-Token-Signature-Key")], true, route.Formats); err != nil { res = append(res, err) } @@ -124,7 +124,7 @@ func (o *PutContainerParams) bindXNeofsTokenSignature(rawData []string, hasKey b // bindXNeofsTokenSignatureKey binds and validates parameter XNeofsTokenSignatureKey from header. func (o *PutContainerParams) bindXNeofsTokenSignatureKey(rawData []string, hasKey bool, formats strfmt.Registry) error { if !hasKey { - return errors.Required("X-Neofs-Token-signature-Key", "header", rawData) + return errors.Required("X-Neofs-Token-Signature-Key", "header", rawData) } var raw string if len(rawData) > 0 { @@ -133,7 +133,7 @@ func (o *PutContainerParams) bindXNeofsTokenSignatureKey(rawData []string, hasKe // Required: true - if err := validate.RequiredString("X-Neofs-Token-signature-Key", "header", raw); err != nil { + if err := validate.RequiredString("X-Neofs-Token-Signature-Key", "header", raw); err != nil { return err } o.XNeofsTokenSignatureKey = raw diff --git a/handlers/api.go b/handlers/api.go index c7bd04d..94e5f6f 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -41,10 +41,10 @@ type BearerToken struct { type ContextKey string const ( - // BearerPrefix is the prefix for authorization token + // BearerPrefix is the prefix for authorization token. BearerPrefix = "Bearer " - // ContextKeyRequestID is the ContextKey for RequestID + // ContextKeyRequestID is the ContextKey for RequestID. ContextKeyRequestID ContextKey = "requestID" ) @@ -62,9 +62,13 @@ func (a *API) Configure(api *operations.NeofsRestGwAPI) http.Handler { api.ServeError = errors.ServeError api.AuthHandler = operations.AuthHandlerFunc(a.PostAuth) + api.PutObjectHandler = operations.PutObjectHandlerFunc(a.PutObjects) + api.GetObjectInfoHandler = operations.GetObjectInfoHandlerFunc(a.GetObjectInfo) + api.PutContainerHandler = operations.PutContainerHandlerFunc(a.PutContainers) api.GetContainerHandler = operations.GetContainerHandlerFunc(a.GetContainer) + api.BearerAuthAuth = func(s string) (*models.Principal, error) { if !strings.HasPrefix(s, BearerPrefix) { return nil, fmt.Errorf("has not bearer token") diff --git a/handlers/containers.go b/handlers/containers.go index 2e937a7..3406ec4 100644 --- a/handlers/containers.go +++ b/handlers/containers.go @@ -62,15 +62,18 @@ func (a *API) GetContainer(params operations.GetContainerParams) middleware.Resp attrs := make([]*models.Attribute, len(cnr.Attributes())) for i, attr := range cnr.Attributes() { - attrs[i] = &models.Attribute{Key: attr.Key(), Value: attr.Value()} + attrs[i] = &models.Attribute{ + Key: NewString(attr.Key()), + Value: NewString(attr.Value()), + } } resp := &models.ContainerInfo{ - ContainerID: params.ContainerID, - Version: cnr.Version().String(), - OwnerID: cnr.OwnerID().String(), - BasicACL: acl.BasicACL(cnr.BasicACL()).String(), - PlacementPolicy: strings.Join(policy.Encode(cnr.PlacementPolicy()), " "), + ContainerID: NewString(params.ContainerID), + Version: NewString(cnr.Version().String()), + OwnerID: NewString(cnr.OwnerID().String()), + BasicACL: NewString(acl.BasicACL(cnr.BasicACL()).String()), + PlacementPolicy: NewString(strings.Join(policy.Encode(cnr.PlacementPolicy()), " ")), Attributes: attrs, } diff --git a/handlers/objects.go b/handlers/objects.go index c82101d..ef04be1 100644 --- a/handlers/objects.go +++ b/handlers/objects.go @@ -4,6 +4,10 @@ import ( "encoding/base64" "fmt" + "github.com/nspcc-dev/neofs-sdk-go/object/address" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + "go.uber.org/zap" + "github.com/go-openapi/runtime/middleware" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neofs-api-go/v2/acl" @@ -18,6 +22,7 @@ import ( // PutObjects handler that uploads object to NeoFS. func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Principal) middleware.Responder { + errorResponse := operations.NewPutObjectBadRequest() ctx := params.HTTPRequest.Context() bt := &BearerToken{ @@ -28,17 +33,18 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr btoken, err := prepareBearerToken(bt) if err != nil { - return operations.NewPutObjectBadRequest().WithPayload(models.Error(err.Error())) + return errorResponse.WithPayload(models.Error(err.Error())) } var cnrID cid.ID if err = cnrID.Parse(*params.Object.ContainerID); err != nil { - return operations.NewPutObjectBadRequest().WithPayload(models.Error(err.Error())) + a.log.Error("invalid container id", zap.Error(err)) + return errorResponse.WithPayload("invalid container id") } payload, err := base64.StdEncoding.DecodeString(params.Object.Payload) if err != nil { - return operations.NewPutObjectBadRequest().WithPayload(models.Error(err.Error())) + return errorResponse.WithPayload(models.Error(err.Error())) } prm := PrmAttributes{ @@ -47,7 +53,7 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr } attributes, err := GetObjectAttributes(ctx, params.HTTPRequest.Header, a.pool, prm) if err != nil { - return operations.NewPutObjectBadRequest().WithPayload(models.Error(err.Error())) + return errorResponse.WithPayload(models.Error(err.Error())) } obj := object.New() @@ -62,7 +68,7 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr objID, err := a.pool.PutObject(ctx, prmPut) if err != nil { - return operations.NewPutObjectBadRequest().WithPayload(models.Error(err.Error())) + return errorResponse.WithPayload(NewError(err)) } var resp operations.PutObjectOKBody @@ -72,6 +78,61 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr return operations.NewPutObjectOK().WithPayload(&resp) } +// GetObjectInfo handler that get object info. +func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *models.Principal) middleware.Responder { + errorResponse := operations.NewGetObjectInfoBadRequest() + ctx := params.HTTPRequest.Context() + + var cnrID cid.ID + if err := cnrID.Parse(params.ContainerID); err != nil { + a.log.Error("invalid container id", zap.Error(err)) + return errorResponse.WithPayload("invalid container id") + } + var objID oid.ID + if err := objID.Parse(params.ObjectID); err != nil { + a.log.Error("invalid object id", zap.Error(err)) + return errorResponse.WithPayload("invalid object id") + } + + bt := &BearerToken{ + Token: string(*principal), + Signature: params.XNeofsTokenSignature, + Key: params.XNeofsTokenSignatureKey, + } + + btoken, err := prepareBearerToken(bt) + if err != nil { + return errorResponse.WithPayload(NewError(err)) + } + + var prm pool.PrmObjectHead + addr := address.NewAddress() + addr.SetContainerID(&cnrID) + addr.SetObjectID(&objID) + prm.SetAddress(*addr) + prm.UseBearer(btoken) + + objInfo, err := a.pool.HeadObject(ctx, prm) + if err != nil { + return errorResponse.WithPayload(NewError(err)) + } + + var resp models.ObjectInfo + resp.ContainerID = NewString(params.ContainerID) + resp.ObjectID = NewString(params.ObjectID) + resp.OwnerID = NewString(objInfo.OwnerID().String()) + resp.Attributes = make([]*models.Attribute, len(objInfo.Attributes())) + + for i, attr := range objInfo.Attributes() { + resp.Attributes[i] = &models.Attribute{ + Key: NewString(attr.Key()), + Value: NewString(attr.Value()), + } + } + + return operations.NewGetObjectInfoOK().WithPayload(&resp) +} + func prepareBearerToken(bt *BearerToken) (*token.BearerToken, error) { btoken, err := getBearerToken(bt.Token) if err != nil { diff --git a/handlers/util.go b/handlers/util.go index d12d2a0..766b977 100644 --- a/handlers/util.go +++ b/handlers/util.go @@ -9,6 +9,8 @@ import ( "strings" "time" + "github.com/nspcc-dev/neofs-rest-gw/gen/models" + objectv2 "github.com/nspcc-dev/neofs-api-go/v2/object" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/object" @@ -216,3 +218,7 @@ func updateExpirationHeader(headers map[string]string, durations *epochDurations func NewString(val string) *string { return &val } + +func NewError(err error) models.Error { + return models.Error(err.Error()) +} diff --git a/spec/rest.yaml b/spec/rest.yaml index 15a9ad1..839f272 100644 --- a/spec/rest.yaml +++ b/spec/rest.yaml @@ -15,10 +15,25 @@ securityDefinitions: type: apiKey in: header name: Authorization + description: Bearer token body to provide with NeoFS request. Must have 'Bearer ' prefix. security: - BearerAuth: [ ] +parameters: + signatureParam: + in: header + name: X-Neofs-Token-Signature + description: Base64 encoded signature for bearer token + type: string + required: true + signatureKeyParam: + in: header + name: X-Neofs-Token-Signature-Key + description: Hex encoded the public part of the key that signed the bearer token + type: string + required: true + paths: /auth: post: @@ -66,20 +81,8 @@ paths: /objects: parameters: - - in: header - name: X-Neofs-Token-Signature - description: Base64 encoded signature for bearer token - type: string - required: true - # example: - # BGtqMEpzxTabrdIIIDAnL79Cs7bm46+8lsFaMMU+LCKw/ujEjF0r5mVLKixWmxoreuj1E0BXWcqp9d3wGV6Hc9I= - - in: header - name: X-Neofs-Token-Signature-Key - description: Hex encoded the public part of the key that signed the bearer token - type: string - required: true - # example: - # 031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a + - $ref: '#/parameters/signatureParam' + - $ref: '#/parameters/signatureKeyParam' put: operationId: putObject summary: Upload object to NeoFS @@ -129,22 +132,37 @@ paths: schema: $ref: '#/definitions/Error' + /objects/{containerId}/{objectId}: + parameters: + - $ref: '#/parameters/signatureParam' + - $ref: '#/parameters/signatureKeyParam' + - in: path + name: containerId + type: string + required: true + description: Base58 encoded container id + - in: path + name: objectId + type: string + required: true + description: Base58 encoded object id + get: + operationId: getObjectInfo + summary: Get object info by address and + responses: + 200: + description: Object info + schema: + $ref: '#/definitions/ObjectInfo' + 400: + description: Bad request + schema: + $ref: '#/definitions/Error' + /containers: parameters: - - in: header - name: X-Neofs-Token-Signature - description: Base64 encoded signature for bearer token - type: string - required: true - # example: - # BEvF1N0heytTXn1p2ZV3jN8YM25YkG4FxHmPeq2kWP5HeHCAN4cDjONyX6Bh30Ypw6Kfch2nYOfhiL+rClYQJ9Q= - - in: header - name: X-Neofs-Token-signature-Key - description: Hex encoded the public part of the key that signed the bearer token - type: string - required: true - # example: - # 031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a + - $ref: '#/parameters/signatureParam' + - $ref: '#/parameters/signatureKeyParam' put: operationId: putContainer summary: Create new container in NeoFS @@ -364,6 +382,13 @@ definitions: type: array items: $ref: '#/definitions/Attribute' + required: + - containerId + - version + - ownerId + - basicAcl + - placementPolicy + - attributes example: containerId: 5HZTn5qkRnmgSz9gSrw22CEdPPk6nQhkwf2Mgzyvkikv version: "2.11" @@ -375,6 +400,34 @@ definitions: value: "1648810072" - key: Name value: container + ObjectInfo: + type: object + properties: + containerId: + type: string + objectId: + type: string + ownerId: + type: string + attributes: + type: array + items: + $ref: '#/definitions/Attribute' + required: + - containerId + - objectId + - ownerId + - attributes + example: + containerId: 5HZTn5qkRnmgSz9gSrw22CEdPPk6nQhkwf2Mgzyvkikv + objectId: 8N3o7Dtr6T1xteCt6eRwhpmJ7JhME58Hyu1dvaswuTDd + ownerId: NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM + attribute: + - key: Timestamp + value: "1648810072" + - key: Name + value: object + Attribute: type: object properties: @@ -382,6 +435,9 @@ definitions: type: string value: type: string + required: + - key + - value Principal: type: string Error: