frostfs-sdk-go/session/object.go

283 lines
6.9 KiB
Go
Raw Permalink Normal View History

package session
import (
"crypto/ecdsa"
"errors"
"fmt"
2023-06-23 12:01:41 +00:00
refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs/grpc"
session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session/grpc"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
// Object represents token of the FrostFS Object session. A session is opened
// between any two sides of the system, and implements a mechanism for transferring
// the power of attorney of actions to another network member. The session has a
// limited validity period, and applies to a strictly defined set of operations.
// See methods for details.
//
// Object is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session.Token
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration.
type Object struct {
commonData
verb ObjectVerb
cnrSet bool
cnr cid.ID
objs []oid.ID
}
2023-06-23 12:01:41 +00:00
func (x *Object) readContext(b *session.SessionToken_Body, checkFieldPresence bool) error {
c := b.GetContext()
if c == nil {
if checkFieldPresence {
return errors.New("missing session context")
}
return nil
}
stbc, ok := c.(*session.SessionToken_Body_Object)
if !ok || stbc == nil {
return fmt.Errorf("invalid context %T", c)
}
2023-06-23 12:01:41 +00:00
cObj := stbc.Object
var err error
2023-06-23 12:01:41 +00:00
cnr := cObj.GetTarget().GetContainer()
if x.cnrSet = cnr != nil; x.cnrSet {
2023-06-23 12:01:41 +00:00
err := x.cnr.ReadFromV2(cnr)
if err != nil {
return fmt.Errorf("invalid container ID: %w", err)
}
} else if checkFieldPresence {
return errors.New("missing target container")
}
2023-06-23 12:01:41 +00:00
objs := cObj.GetTarget().GetObjects()
if objs != nil {
x.objs = make([]oid.ID, len(objs))
for i := range objs {
err = x.objs[i].ReadFromV2(objs[i])
if err != nil {
return fmt.Errorf("invalid target object: %w", err)
}
}
} else {
x.objs = nil
}
x.verb = ObjectVerb(cObj.GetVerb())
return nil
}
2023-06-23 12:01:41 +00:00
func (x *Object) readFromV2(m *session.SessionToken, checkFieldPresence bool) error {
return x.commonData.readFromV2(m, checkFieldPresence, x.readContext)
}
// ReadFromV2 reads Object from the session.Token message. Checks if the
// message conforms to FrostFS API V2 protocol.
//
// See also WriteToV2.
2023-06-23 12:01:41 +00:00
func (x *Object) ReadFromV2(m *session.SessionToken) error {
return x.readFromV2(m, true)
}
2023-06-23 12:01:41 +00:00
func (x Object) writeContext(b *session.SessionToken_Body) {
c := new(session.SessionToken_Body_Object)
c.Object = new(session.ObjectSessionContext)
c.Object.SetVerb(session.ObjectSessionContext_Verb(x.verb))
if x.cnrSet || len(x.objs) > 0 {
var cnr *refs.ContainerID
if x.cnrSet {
cnr = new(refs.ContainerID)
x.cnr.WriteToV2(cnr)
}
2023-06-23 12:01:41 +00:00
var objs []*refs.ObjectID
if x.objs != nil {
2023-06-23 12:01:41 +00:00
objs = make([]*refs.ObjectID, len(x.objs))
for i := range x.objs {
2023-06-23 12:01:41 +00:00
objs[i] = new(refs.ObjectID)
x.objs[i].WriteToV2(objs[i])
}
}
2023-06-23 12:01:41 +00:00
c.Object.SetTarget(cnr, objs)
}
2023-06-23 12:01:41 +00:00
b.Context = c
}
// WriteToV2 writes Object to the session.Token message.
// The message must not be nil.
//
// See also ReadFromV2.
2023-06-23 12:01:41 +00:00
func (x Object) WriteToV2(m *session.SessionToken) {
x.writeToV2(m, x.writeContext)
}
// Marshal encodes Object into a binary format of the FrostFS API protocol
// (Protocol Buffers with direct field order).
//
// See also Unmarshal.
func (x Object) Marshal() []byte {
2023-06-23 12:01:41 +00:00
var m session.SessionToken
x.WriteToV2(&m)
return x.marshal(x.writeContext)
}
// Unmarshal decodes FrostFS API protocol binary format into the Object
// (Protocol Buffers with direct field order). Returns an error describing
// a format violation.
//
// See also Marshal.
func (x *Object) Unmarshal(data []byte) error {
return x.unmarshal(data, x.readContext)
}
// MarshalJSON encodes Object into a JSON format of the FrostFS API protocol
// (Protocol Buffers JSON).
//
// See also UnmarshalJSON.
func (x Object) MarshalJSON() ([]byte, error) {
return x.marshalJSON(x.writeContext)
}
// UnmarshalJSON decodes FrostFS API protocol JSON format into the Object
// (Protocol Buffers JSON). Returns an error describing a format violation.
//
// See also MarshalJSON.
func (x *Object) UnmarshalJSON(data []byte) error {
return x.unmarshalJSON(data, x.readContext)
}
// Sign calculates and writes signature of the Object data.
// Returns signature calculation errors.
//
// Zero Object is unsigned.
//
// Note that any Object mutation is likely to break the signature, so it is
// expected to be calculated as a final stage of Object formation.
//
// See also VerifySignature.
func (x *Object) Sign(key ecdsa.PrivateKey) error {
return x.sign(key, x.writeContext)
}
// VerifySignature checks if Object signature is presented and valid.
//
// Zero Object fails the check.
//
// See also Sign.
func (x Object) VerifySignature() bool {
// TODO: (#233) check owner<->key relation
return x.verifySignature(x.writeContext)
}
// BindContainer binds the Object session to a given container. Each session
// MUST be bound to exactly one container.
//
// See also AssertContainer.
func (x *Object) BindContainer(cnr cid.ID) {
x.cnr = cnr
x.cnrSet = true
}
// AssertContainer checks if Object session bound to a given container.
//
// Zero Object isn't bound to any container which is incorrect according to
// FrostFS API protocol.
//
// See also BindContainer.
func (x Object) AssertContainer(cnr cid.ID) bool {
return x.cnr.Equals(cnr)
}
// LimitByObjects limits session scope to the given objects from the container
// to which Object session is bound.
//
// Argument MUST NOT be mutated, make a copy first.
//
// See also AssertObject.
func (x *Object) LimitByObjects(objs ...oid.ID) {
x.objs = objs
}
// AssertObject checks if Object session is applied to a given object.
//
// Zero Object is applied to all objects in the container.
//
// See also LimitByObjects.
func (x Object) AssertObject(obj oid.ID) bool {
if len(x.objs) == 0 {
return true
}
for i := range x.objs {
if x.objs[i].Equals(obj) {
return true
}
}
return false
}
// ObjectVerb enumerates object operations.
type ObjectVerb int8
const (
_ ObjectVerb = iota
VerbObjectPut // Put rpc
VerbObjectGet // Get rpc
VerbObjectHead // Head rpc
VerbObjectSearch // Search rpc
VerbObjectDelete // Delete rpc
VerbObjectRange // GetRange rpc
VerbObjectRangeHash // GetRangeHash rpc
)
// ForVerb specifies the object operation of the session scope. Each
// Object is related to the single operation.
//
// See also AssertVerb.
func (x *Object) ForVerb(verb ObjectVerb) {
x.verb = verb
}
// AssertVerb checks if Object relates to one of the given object operations.
//
// Zero Object relates to zero (unspecified) verb.
//
// See also ForVerb.
func (x Object) AssertVerb(verbs ...ObjectVerb) bool {
for i := range verbs {
if verbs[i] == x.verb {
return true
}
}
return false
}
// ExpiredAt asserts "exp" claim.
//
// Zero Object is expired in any epoch.
//
// See also SetExp.
func (x Object) ExpiredAt(epoch uint64) bool {
return x.expiredAt(epoch)
}