git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/session/container.go (about)

     1  package session
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
     9  	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
    10  	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
    11  	frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
    12  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
    13  )
    14  
    15  // Container represents token of the FrostFS Container session. A session is opened
    16  // between any two sides of the system, and implements a mechanism for transferring
    17  // the power of attorney of actions to another network member. The session has a
    18  // limited validity period, and applies to a strictly defined set of operations.
    19  // See methods for details.
    20  //
    21  // Container is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session.Token
    22  // message. See ReadFromV2 / WriteToV2 methods.
    23  //
    24  // Instances can be created using built-in var declaration.
    25  type Container struct {
    26  	commonData
    27  
    28  	verb ContainerVerb
    29  
    30  	cnrSet bool
    31  	cnr    cid.ID
    32  }
    33  
    34  // readContext is a contextReader needed for commonData methods.
    35  func (x *Container) readContext(c session.TokenContext, checkFieldPresence bool) error {
    36  	cCnr, ok := c.(*session.ContainerSessionContext)
    37  	if !ok || cCnr == nil {
    38  		return fmt.Errorf("invalid context %T", c)
    39  	}
    40  
    41  	x.cnrSet = !cCnr.Wildcard()
    42  	cnr := cCnr.ContainerID()
    43  
    44  	if x.cnrSet {
    45  		if cnr != nil {
    46  			err := x.cnr.ReadFromV2(*cnr)
    47  			if err != nil {
    48  				return fmt.Errorf("invalid container ID: %w", err)
    49  			}
    50  		} else if checkFieldPresence {
    51  			return errors.New("missing container or wildcard flag")
    52  		}
    53  	} else if cnr != nil {
    54  		return errors.New("container conflicts with wildcard flag")
    55  	}
    56  
    57  	x.verb = ContainerVerb(cCnr.Verb())
    58  
    59  	return nil
    60  }
    61  
    62  func (x *Container) readFromV2(m session.Token, checkFieldPresence bool) error {
    63  	return x.commonData.readFromV2(m, checkFieldPresence, x.readContext)
    64  }
    65  
    66  // ReadFromV2 reads Container from the session.Token message. Checks if the
    67  // message conforms to FrostFS API V2 protocol.
    68  //
    69  // See also WriteToV2.
    70  func (x *Container) ReadFromV2(m session.Token) error {
    71  	return x.readFromV2(m, true)
    72  }
    73  
    74  func (x Container) writeContext() session.TokenContext {
    75  	var c session.ContainerSessionContext
    76  	c.SetWildcard(!x.cnrSet)
    77  	c.SetVerb(session.ContainerSessionVerb(x.verb))
    78  
    79  	if x.cnrSet {
    80  		var cnr refs.ContainerID
    81  		x.cnr.WriteToV2(&cnr)
    82  
    83  		c.SetContainerID(&cnr)
    84  	}
    85  
    86  	return &c
    87  }
    88  
    89  // WriteToV2 writes Container to the session.Token message.
    90  // The message must not be nil.
    91  //
    92  // See also ReadFromV2.
    93  func (x Container) WriteToV2(m *session.Token) {
    94  	x.writeToV2(m, x.writeContext)
    95  }
    96  
    97  // Marshal encodes Container into a binary format of the FrostFS API protocol
    98  // (Protocol Buffers with direct field order).
    99  //
   100  // See also Unmarshal.
   101  func (x Container) Marshal() []byte {
   102  	return x.marshal(x.writeContext)
   103  }
   104  
   105  // Unmarshal decodes FrostFS API protocol binary format into the Container
   106  // (Protocol Buffers with direct field order). Returns an error describing
   107  // a format violation.
   108  //
   109  // See also Marshal.
   110  func (x *Container) Unmarshal(data []byte) error {
   111  	return x.unmarshal(data, x.readContext)
   112  }
   113  
   114  // MarshalJSON encodes Container into a JSON format of the FrostFS API protocol
   115  // (Protocol Buffers JSON).
   116  //
   117  // See also UnmarshalJSON.
   118  func (x Container) MarshalJSON() ([]byte, error) {
   119  	return x.marshalJSON(x.writeContext)
   120  }
   121  
   122  // UnmarshalJSON decodes FrostFS API protocol JSON format into the Container
   123  // (Protocol Buffers JSON). Returns an error describing a format violation.
   124  //
   125  // See also MarshalJSON.
   126  func (x *Container) UnmarshalJSON(data []byte) error {
   127  	return x.unmarshalJSON(data, x.readContext)
   128  }
   129  
   130  // Sign calculates and writes signature of the Container data.
   131  // Returns signature calculation errors.
   132  //
   133  // Zero Container is unsigned.
   134  //
   135  // Note that any Container mutation is likely to break the signature, so it is
   136  // expected to be calculated as a final stage of Container formation.
   137  //
   138  // See also VerifySignature.
   139  func (x *Container) Sign(key ecdsa.PrivateKey) error {
   140  	return x.sign(key, x.writeContext)
   141  }
   142  
   143  // VerifySignature checks if Container signature is presented and valid.
   144  //
   145  // Zero Container fails the check.
   146  //
   147  // See also Sign.
   148  func (x Container) VerifySignature() bool {
   149  	return x.verifySignature(x.writeContext)
   150  }
   151  
   152  // ApplyOnlyTo limits session scope to a given author container.
   153  //
   154  // See also AppliedTo.
   155  func (x *Container) ApplyOnlyTo(cnr cid.ID) {
   156  	x.cnr = cnr
   157  	x.cnrSet = true
   158  }
   159  
   160  // AppliedTo checks if the session is propagated to the given container.
   161  //
   162  // Zero Container is applied to all author's containers.
   163  //
   164  // See also ApplyOnlyTo.
   165  func (x Container) AppliedTo(cnr cid.ID) bool {
   166  	return !x.cnrSet || x.cnr.Equals(cnr)
   167  }
   168  
   169  // ContainerVerb enumerates container operations.
   170  type ContainerVerb int8
   171  
   172  const (
   173  	_ ContainerVerb = iota
   174  
   175  	VerbContainerPut     // Put rpc
   176  	VerbContainerDelete  // Delete rpc
   177  	VerbContainerSetEACL // SetExtendedACL rpc
   178  )
   179  
   180  // ForVerb specifies the container operation of the session scope. Each
   181  // Container is related to the single operation.
   182  //
   183  // See also AssertVerb.
   184  func (x *Container) ForVerb(verb ContainerVerb) {
   185  	x.verb = verb
   186  }
   187  
   188  // AssertVerb checks if Container relates to the given container operation.
   189  //
   190  // Zero Container relates to zero (unspecified) verb.
   191  //
   192  // See also ForVerb.
   193  func (x Container) AssertVerb(verb ContainerVerb) bool {
   194  	return x.verb == verb
   195  }
   196  
   197  // IssuedBy checks if Container session is issued by the given user.
   198  //
   199  // See also Container.Issuer.
   200  func IssuedBy(cnr Container, id user.ID) bool {
   201  	return cnr.Issuer().Equals(id)
   202  }
   203  
   204  // VerifySessionDataSignature verifies signature of the session data. In practice,
   205  // the method is used to authenticate an operation with session data.
   206  func (x Container) VerifySessionDataSignature(data, signature []byte) bool {
   207  	var sigV2 refs.Signature
   208  	sigV2.SetKey(x.authKey)
   209  	sigV2.SetScheme(refs.ECDSA_RFC6979_SHA256)
   210  	sigV2.SetSign(signature)
   211  
   212  	var sig frostfscrypto.Signature
   213  
   214  	return sig.ReadFromV2(sigV2) == nil && sig.Verify(data)
   215  }