frostfs-sdk-go/session/container.go

216 lines
5.9 KiB
Go

package session
import (
"crypto/ecdsa"
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)
// Container represents token of the FrostFS Container 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.
//
// Container 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 Container struct {
commonData
verb ContainerVerb
cnrSet bool
cnr cid.ID
}
// readContext is a contextReader needed for commonData methods.
func (x *Container) readContext(c session.TokenContext, checkFieldPresence bool) error {
cCnr, ok := c.(*session.ContainerSessionContext)
if !ok || cCnr == nil {
return fmt.Errorf("invalid context %T", c)
}
x.cnrSet = !cCnr.Wildcard()
cnr := cCnr.ContainerID()
if x.cnrSet {
if cnr != nil {
err := x.cnr.ReadFromV2(*cnr)
if err != nil {
return fmt.Errorf("invalid container ID: %w", err)
}
} else if checkFieldPresence {
return errors.New("missing container or wildcard flag")
}
} else if cnr != nil {
return errors.New("container conflicts with wildcard flag")
}
x.verb = ContainerVerb(cCnr.Verb())
return nil
}
func (x *Container) readFromV2(m session.Token, checkFieldPresence bool) error {
return x.commonData.readFromV2(m, checkFieldPresence, x.readContext)
}
// ReadFromV2 reads Container from the session.Token message. Checks if the
// message conforms to FrostFS API V2 protocol.
//
// See also WriteToV2.
func (x *Container) ReadFromV2(m session.Token) error {
return x.readFromV2(m, true)
}
func (x Container) writeContext() session.TokenContext {
var c session.ContainerSessionContext
c.SetWildcard(!x.cnrSet)
c.SetVerb(session.ContainerSessionVerb(x.verb))
if x.cnrSet {
var cnr refs.ContainerID
x.cnr.WriteToV2(&cnr)
c.SetContainerID(&cnr)
}
return &c
}
// WriteToV2 writes Container to the session.Token message.
// The message must not be nil.
//
// See also ReadFromV2.
func (x Container) WriteToV2(m *session.Token) {
x.writeToV2(m, x.writeContext)
}
// Marshal encodes Container into a binary format of the FrostFS API protocol
// (Protocol Buffers with direct field order).
//
// See also Unmarshal.
func (x Container) Marshal() []byte {
return x.marshal(x.writeContext)
}
// Unmarshal decodes FrostFS API protocol binary format into the Container
// (Protocol Buffers with direct field order). Returns an error describing
// a format violation.
//
// See also Marshal.
func (x *Container) Unmarshal(data []byte) error {
return x.unmarshal(data, x.readContext)
}
// MarshalJSON encodes Container into a JSON format of the FrostFS API protocol
// (Protocol Buffers JSON).
//
// See also UnmarshalJSON.
func (x Container) MarshalJSON() ([]byte, error) {
return x.marshalJSON(x.writeContext)
}
// UnmarshalJSON decodes FrostFS API protocol JSON format into the Container
// (Protocol Buffers JSON). Returns an error describing a format violation.
//
// See also MarshalJSON.
func (x *Container) UnmarshalJSON(data []byte) error {
return x.unmarshalJSON(data, x.readContext)
}
// Sign calculates and writes signature of the Container data.
// Returns signature calculation errors.
//
// Zero Container is unsigned.
//
// Note that any Container mutation is likely to break the signature, so it is
// expected to be calculated as a final stage of Container formation.
//
// See also VerifySignature.
func (x *Container) Sign(key ecdsa.PrivateKey) error {
return x.sign(key, x.writeContext)
}
// VerifySignature checks if Container signature is presented and valid.
//
// Zero Container fails the check.
//
// See also Sign.
func (x Container) VerifySignature() bool {
return x.verifySignature(x.writeContext)
}
// ApplyOnlyTo limits session scope to a given author container.
//
// See also AppliedTo.
func (x *Container) ApplyOnlyTo(cnr cid.ID) {
x.cnr = cnr
x.cnrSet = true
}
// AppliedTo checks if the session is propagated to the given container.
//
// Zero Container is applied to all author's containers.
//
// See also ApplyOnlyTo.
func (x Container) AppliedTo(cnr cid.ID) bool {
return !x.cnrSet || x.cnr.Equals(cnr)
}
// ContainerVerb enumerates container operations.
type ContainerVerb int8
const (
_ ContainerVerb = iota
VerbContainerPut // Put rpc
VerbContainerDelete // Delete rpc
VerbContainerSetEACL // SetExtendedACL rpc
)
// ForVerb specifies the container operation of the session scope. Each
// Container is related to the single operation.
//
// See also AssertVerb.
func (x *Container) ForVerb(verb ContainerVerb) {
x.verb = verb
}
// AssertVerb checks if Container relates to the given container operation.
//
// Zero Container relates to zero (unspecified) verb.
//
// See also ForVerb.
func (x Container) AssertVerb(verb ContainerVerb) bool {
return x.verb == verb
}
// IssuedBy checks if Container session is issued by the given user.
//
// See also Container.Issuer.
func IssuedBy(cnr Container, id user.ID) bool {
return cnr.Issuer().Equals(id)
}
// VerifySessionDataSignature verifies signature of the session data. In practice,
// the method is used to authenticate an operation with session data.
func (x Container) VerifySessionDataSignature(data, signature []byte) bool {
var sigV2 refs.Signature
sigV2.SetKey(x.authKey)
sigV2.SetScheme(refs.ECDSA_RFC6979_SHA256)
sigV2.SetSign(signature)
var sig frostfscrypto.Signature
return sig.ReadFromV2(sigV2) == nil && sig.Verify(data)
}