git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/session/object.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 oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" 12 ) 13 14 // Object represents token of the FrostFS Object session. A session is opened 15 // between any two sides of the system, and implements a mechanism for transferring 16 // the power of attorney of actions to another network member. The session has a 17 // limited validity period, and applies to a strictly defined set of operations. 18 // See methods for details. 19 // 20 // Object is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session.Token 21 // message. See ReadFromV2 / WriteToV2 methods. 22 // 23 // Instances can be created using built-in var declaration. 24 type Object struct { 25 commonData 26 27 verb ObjectVerb 28 29 cnrSet bool 30 cnr cid.ID 31 32 objs []oid.ID 33 } 34 35 func (x *Object) readContext(c session.TokenContext, checkFieldPresence bool) error { 36 cObj, ok := c.(*session.ObjectSessionContext) 37 if !ok || cObj == nil { 38 return fmt.Errorf("invalid context %T", c) 39 } 40 41 var err error 42 43 cnr := cObj.GetContainer() 44 if x.cnrSet = cnr != nil; x.cnrSet { 45 err := x.cnr.ReadFromV2(*cnr) 46 if err != nil { 47 return fmt.Errorf("invalid container ID: %w", err) 48 } 49 } else if checkFieldPresence { 50 return errors.New("missing target container") 51 } 52 53 objs := cObj.GetObjects() 54 if objs != nil { 55 x.objs = make([]oid.ID, len(objs)) 56 57 for i := range objs { 58 err = x.objs[i].ReadFromV2(objs[i]) 59 if err != nil { 60 return fmt.Errorf("invalid target object: %w", err) 61 } 62 } 63 } else { 64 x.objs = nil 65 } 66 67 x.verb = ObjectVerb(cObj.GetVerb()) 68 69 return nil 70 } 71 72 func (x *Object) readFromV2(m session.Token, checkFieldPresence bool) error { 73 return x.commonData.readFromV2(m, checkFieldPresence, x.readContext) 74 } 75 76 // ReadFromV2 reads Object from the session.Token message. Checks if the 77 // message conforms to FrostFS API V2 protocol. 78 // 79 // See also WriteToV2. 80 func (x *Object) ReadFromV2(m session.Token) error { 81 return x.readFromV2(m, true) 82 } 83 84 func (x Object) writeContext() session.TokenContext { 85 var c session.ObjectSessionContext 86 c.SetVerb(session.ObjectSessionVerb(x.verb)) 87 88 if x.cnrSet || len(x.objs) > 0 { 89 var cnr *refs.ContainerID 90 91 if x.cnrSet { 92 cnr = new(refs.ContainerID) 93 x.cnr.WriteToV2(cnr) 94 } 95 96 var objs []refs.ObjectID 97 98 if x.objs != nil { 99 objs = make([]refs.ObjectID, len(x.objs)) 100 101 for i := range x.objs { 102 x.objs[i].WriteToV2(&objs[i]) 103 } 104 } 105 106 c.SetTarget(cnr, objs...) 107 } 108 109 return &c 110 } 111 112 // WriteToV2 writes Object to the session.Token message. 113 // The message must not be nil. 114 // 115 // See also ReadFromV2. 116 func (x Object) WriteToV2(m *session.Token) { 117 x.writeToV2(m, x.writeContext) 118 } 119 120 // Marshal encodes Object into a binary format of the FrostFS API protocol 121 // (Protocol Buffers with direct field order). 122 // 123 // See also Unmarshal. 124 func (x Object) Marshal() []byte { 125 var m session.Token 126 x.WriteToV2(&m) 127 128 return x.marshal(x.writeContext) 129 } 130 131 // Unmarshal decodes FrostFS API protocol binary format into the Object 132 // (Protocol Buffers with direct field order). Returns an error describing 133 // a format violation. 134 // 135 // See also Marshal. 136 func (x *Object) Unmarshal(data []byte) error { 137 return x.unmarshal(data, x.readContext) 138 } 139 140 // MarshalJSON encodes Object into a JSON format of the FrostFS API protocol 141 // (Protocol Buffers JSON). 142 // 143 // See also UnmarshalJSON. 144 func (x Object) MarshalJSON() ([]byte, error) { 145 return x.marshalJSON(x.writeContext) 146 } 147 148 // UnmarshalJSON decodes FrostFS API protocol JSON format into the Object 149 // (Protocol Buffers JSON). Returns an error describing a format violation. 150 // 151 // See also MarshalJSON. 152 func (x *Object) UnmarshalJSON(data []byte) error { 153 return x.unmarshalJSON(data, x.readContext) 154 } 155 156 // Sign calculates and writes signature of the Object data. 157 // Returns signature calculation errors. 158 // 159 // Zero Object is unsigned. 160 // 161 // Note that any Object mutation is likely to break the signature, so it is 162 // expected to be calculated as a final stage of Object formation. 163 // 164 // See also VerifySignature. 165 func (x *Object) Sign(key ecdsa.PrivateKey) error { 166 return x.sign(key, x.writeContext) 167 } 168 169 // VerifySignature checks if Object signature is presented and valid. 170 // 171 // Zero Object fails the check. 172 // 173 // See also Sign. 174 func (x Object) VerifySignature() bool { 175 // TODO: (#233) check owner<->key relation 176 return x.verifySignature(x.writeContext) 177 } 178 179 // BindContainer binds the Object session to a given container. Each session 180 // MUST be bound to exactly one container. 181 // 182 // See also AssertContainer. 183 func (x *Object) BindContainer(cnr cid.ID) { 184 x.cnr = cnr 185 x.cnrSet = true 186 } 187 188 // AssertContainer checks if Object session bound to a given container. 189 // 190 // Zero Object isn't bound to any container which is incorrect according to 191 // FrostFS API protocol. 192 // 193 // See also BindContainer. 194 func (x Object) AssertContainer(cnr cid.ID) bool { 195 return x.cnr.Equals(cnr) 196 } 197 198 // LimitByObjects limits session scope to the given objects from the container 199 // to which Object session is bound. 200 // 201 // Argument MUST NOT be mutated, make a copy first. 202 // 203 // See also AssertObject. 204 func (x *Object) LimitByObjects(objs ...oid.ID) { 205 x.objs = objs 206 } 207 208 // AssertObject checks if Object session is applied to a given object. 209 // 210 // Zero Object is applied to all objects in the container. 211 // 212 // See also LimitByObjects. 213 func (x Object) AssertObject(obj oid.ID) bool { 214 if len(x.objs) == 0 { 215 return true 216 } 217 218 for i := range x.objs { 219 if x.objs[i].Equals(obj) { 220 return true 221 } 222 } 223 224 return false 225 } 226 227 // ObjectVerb enumerates object operations. 228 type ObjectVerb int8 229 230 const ( 231 _ ObjectVerb = iota 232 233 VerbObjectPut // Put rpc 234 VerbObjectGet // Get rpc 235 VerbObjectHead // Head rpc 236 VerbObjectSearch // Search rpc 237 VerbObjectDelete // Delete rpc 238 VerbObjectRange // GetRange rpc 239 VerbObjectRangeHash // GetRangeHash rpc 240 VerbObjectPatch // Patch rpc 241 ) 242 243 // ForVerb specifies the object operation of the session scope. Each 244 // Object is related to the single operation. 245 // 246 // See also AssertVerb. 247 func (x *Object) ForVerb(verb ObjectVerb) { 248 x.verb = verb 249 } 250 251 // AssertVerb checks if Object relates to one of the given object operations. 252 // 253 // Zero Object relates to zero (unspecified) verb. 254 // 255 // See also ForVerb. 256 func (x Object) AssertVerb(verbs ...ObjectVerb) bool { 257 for i := range verbs { 258 if verbs[i] == x.verb { 259 return true 260 } 261 } 262 263 return false 264 } 265 266 // ExpiredAt asserts "exp" claim. 267 // 268 // Zero Object is expired in any epoch. 269 // 270 // See also SetExp. 271 func (x Object) ExpiredAt(epoch uint64) bool { 272 return x.expiredAt(epoch) 273 }