git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/bearer/bearer.go (about) 1 package bearer 2 3 import ( 4 "crypto/ecdsa" 5 "errors" 6 "fmt" 7 8 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" 9 apeV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/ape" 10 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" 11 apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape" 12 cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" 13 frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" 14 frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" 15 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl" 16 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" 17 ) 18 19 // Token represents bearer token for object service operations. 20 // 21 // Token is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl.BearerToken 22 // message. See ReadFromV2 / WriteToV2 methods. 23 // 24 // Instances can be created using built-in var declaration. 25 type Token struct { 26 targetUserSet bool 27 targetUser user.ID 28 29 eaclTableSet bool 30 eaclTable eacl.Table 31 32 lifetimeSet bool 33 iat, nbf, exp uint64 34 35 sigSet bool 36 sig refs.Signature 37 38 apeOverrideSet bool 39 apeOverride APEOverride 40 41 impersonate bool 42 } 43 44 // APEOverride is the list of APE chains defined for a target. 45 // These chains are meant to serve as overrides to the already defined (or even undefined) 46 // APE chains for the target (see contract `Policy`). 47 // 48 // The server-side processing of the bearer token with set APE overrides must verify if a client is permitted 49 // to override chains for the target, preventing unauthorized access through the APE mechanism. 50 type APEOverride struct { 51 // Target for which chains are applied. 52 Target apeSDK.ChainTarget 53 54 // The list of APE chains. 55 Chains []apeSDK.Chain 56 } 57 58 // Marshal marshals APEOverride into a protobuf binary form. 59 func (c *APEOverride) Marshal() ([]byte, error) { 60 return c.ToV2().StableMarshal(nil), nil 61 } 62 63 // Unmarshal unmarshals protobuf binary representation of APEOverride. 64 func (c *APEOverride) Unmarshal(data []byte) error { 65 overrideV2 := new(acl.APEOverride) 66 if err := overrideV2.Unmarshal(data); err != nil { 67 return err 68 } 69 70 return c.FromV2(overrideV2) 71 } 72 73 // MarshalJSON encodes APEOverride to protobuf JSON format. 74 func (c *APEOverride) MarshalJSON() ([]byte, error) { 75 return c.ToV2().MarshalJSON() 76 } 77 78 // UnmarshalJSON decodes APEOverride from protobuf JSON format. 79 func (c *APEOverride) UnmarshalJSON(data []byte) error { 80 overrideV2 := new(acl.APEOverride) 81 if err := overrideV2.UnmarshalJSON(data); err != nil { 82 return err 83 } 84 85 return c.FromV2(overrideV2) 86 } 87 88 func (c *APEOverride) FromV2(tokenAPEChains *acl.APEOverride) error { 89 c.Target.FromV2(tokenAPEChains.GetTarget()) 90 if chains := tokenAPEChains.GetChains(); len(chains) > 0 { 91 c.Chains = make([]apeSDK.Chain, len(chains)) 92 for i := range chains { 93 if err := c.Chains[i].ReadFromV2(chains[i]); err != nil { 94 return fmt.Errorf("invalid APE chain: %w", err) 95 } 96 } 97 } 98 return nil 99 } 100 101 func (c *APEOverride) ToV2() *acl.APEOverride { 102 if c == nil { 103 return nil 104 } 105 106 apeOverride := new(acl.APEOverride) 107 apeOverride.SetTarget(c.Target.ToV2()) 108 chains := make([]*apeV2.Chain, len(c.Chains)) 109 for i := range c.Chains { 110 chains[i] = c.Chains[i].ToV2() 111 } 112 apeOverride.SetChains(chains) 113 114 return apeOverride 115 } 116 117 // reads Token from the acl.BearerToken message. If checkFieldPresence is set, 118 // returns an error on absence of any protocol-required field. 119 func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error { 120 var err error 121 122 body := m.GetBody() 123 if checkFieldPresence && body == nil { 124 return errors.New("missing token body") 125 } 126 127 b.impersonate = body.GetImpersonate() 128 129 apeOverrides := body.GetAPEOverride() 130 eaclTable := body.GetEACL() 131 if b.eaclTableSet = eaclTable != nil; b.eaclTableSet { 132 b.eaclTable = *eacl.NewTableFromV2(eaclTable) 133 } else if checkFieldPresence && !b.impersonate && apeOverrides == nil { 134 return errors.New("missing eACL table") 135 } 136 137 targetUser := body.GetOwnerID() 138 if b.targetUserSet = targetUser != nil; b.targetUserSet { 139 err = b.targetUser.ReadFromV2(*targetUser) 140 if err != nil { 141 return fmt.Errorf("invalid target user: %w", err) 142 } 143 } 144 145 lifetime := body.GetLifetime() 146 if b.lifetimeSet = lifetime != nil; b.lifetimeSet { 147 b.iat = lifetime.GetIat() 148 b.nbf = lifetime.GetNbf() 149 b.exp = lifetime.GetExp() 150 } else if checkFieldPresence { 151 return errors.New("missing token lifetime") 152 } 153 154 if b.apeOverrideSet = apeOverrides != nil; b.apeOverrideSet { 155 if err = b.apeOverride.FromV2(apeOverrides); err != nil { 156 return err 157 } 158 } else if checkFieldPresence && !b.impersonate && !b.eaclTableSet { 159 return errors.New("missing APE override") 160 } 161 162 sig := m.GetSignature() 163 if b.sigSet = sig != nil; sig != nil { 164 b.sig = *sig 165 } else if checkFieldPresence { 166 return errors.New("missing body signature") 167 } 168 169 return nil 170 } 171 172 // ReadFromV2 reads Token from the acl.BearerToken message. 173 // 174 // See also WriteToV2. 175 func (b *Token) ReadFromV2(m acl.BearerToken) error { 176 return b.readFromV2(m, true) 177 } 178 179 func (b Token) fillBody() *acl.BearerTokenBody { 180 if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet && !b.impersonate && !b.apeOverrideSet { 181 return nil 182 } 183 184 var body acl.BearerTokenBody 185 186 if b.eaclTableSet { 187 body.SetEACL(b.eaclTable.ToV2()) 188 } 189 190 if b.targetUserSet { 191 var targetUser refs.OwnerID 192 b.targetUser.WriteToV2(&targetUser) 193 194 body.SetOwnerID(&targetUser) 195 } 196 197 if b.lifetimeSet { 198 var lifetime acl.TokenLifetime 199 lifetime.SetIat(b.iat) 200 lifetime.SetNbf(b.nbf) 201 lifetime.SetExp(b.exp) 202 203 body.SetLifetime(&lifetime) 204 } 205 206 if b.apeOverrideSet { 207 body.SetAPEOverride(b.apeOverride.ToV2()) 208 } 209 210 body.SetImpersonate(b.impersonate) 211 212 return &body 213 } 214 215 func (b Token) signedData() []byte { 216 return b.fillBody().StableMarshal(nil) 217 } 218 219 // WriteToV2 writes Token to the acl.BearerToken message. 220 // The message must not be nil. 221 // 222 // See also ReadFromV2. 223 func (b Token) WriteToV2(m *acl.BearerToken) { 224 m.SetBody(b.fillBody()) 225 226 var sig *refs.Signature 227 228 if b.sigSet { 229 sig = &b.sig 230 } 231 232 m.SetSignature(sig) 233 } 234 235 // SetExp sets "exp" (expiration time) claim which identifies the 236 // expiration time (in FrostFS epochs) after which the Token MUST NOT be 237 // accepted for processing. The processing of the "exp" claim requires 238 // that the current epoch MUST be before or equal to the expiration epoch 239 // listed in the "exp" claim. 240 // 241 // Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4. 242 // 243 // See also InvalidAt. 244 func (b *Token) SetExp(exp uint64) { 245 b.exp = exp 246 b.lifetimeSet = true 247 } 248 249 // SetNbf sets "nbf" (not before) claim which identifies the time (in 250 // FrostFS epochs) before which the Token MUST NOT be accepted for processing. The 251 // processing of the "nbf" claim requires that the current epoch MUST be 252 // after or equal to the not-before epoch listed in the "nbf" claim. 253 // 254 // Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5. 255 // 256 // See also InvalidAt. 257 func (b *Token) SetNbf(nbf uint64) { 258 b.nbf = nbf 259 b.lifetimeSet = true 260 } 261 262 // SetIat sets "iat" (issued at) claim which identifies the time (in FrostFS 263 // epochs) at which the Token was issued. This claim can be used to determine 264 // the age of the Token. 265 // 266 // Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6. 267 // 268 // See also InvalidAt. 269 func (b *Token) SetIat(iat uint64) { 270 b.iat = iat 271 b.lifetimeSet = true 272 } 273 274 // InvalidAt asserts "exp", "nbf" and "iat" claims for the given epoch. 275 // 276 // Zero Container is invalid in any epoch. 277 // 278 // See also SetExp, SetNbf, SetIat. 279 func (b Token) InvalidAt(epoch uint64) bool { 280 return !b.lifetimeSet || b.nbf > epoch || b.iat > epoch || b.exp < epoch 281 } 282 283 // SetEACLTable sets eacl.Table that replaces the one from the issuer's 284 // container. If table has specified container, bearer token can be used only 285 // for operations within this specific container. Otherwise, Token can be used 286 // within any issuer's container. 287 // 288 // SetEACLTable MUST be called if Token is going to be transmitted over 289 // FrostFS API V2 protocol. 290 // 291 // See also EACLTable, AssertContainer. 292 func (b *Token) SetEACLTable(table eacl.Table) { 293 b.eaclTable = table 294 b.eaclTableSet = true 295 } 296 297 // EACLTable returns extended ACL table set by SetEACLTable. 298 // 299 // Zero Token has zero eacl.Table. 300 func (b Token) EACLTable() eacl.Table { 301 if b.eaclTableSet { 302 return b.eaclTable 303 } 304 305 return eacl.Table{} 306 } 307 308 // SetAPEOverride sets APE override to the bearer token. 309 // 310 // See also: APEOverride. 311 func (b *Token) SetAPEOverride(v APEOverride) { 312 b.apeOverride = v 313 b.apeOverrideSet = true 314 } 315 316 // APEOverride returns APE override set by SetAPEOverride. 317 // 318 // Zero Token has zero APEOverride. 319 func (b *Token) APEOverride() APEOverride { 320 if b.apeOverrideSet { 321 return b.apeOverride 322 } 323 324 return APEOverride{} 325 } 326 327 // SetImpersonate mark token as impersonate to consider token signer as request owner. 328 // If this field is true extended EACLTable in token body isn't processed. 329 func (b *Token) SetImpersonate(v bool) { 330 b.impersonate = v 331 } 332 333 // Impersonate returns true if token is impersonated. 334 func (b Token) Impersonate() bool { 335 return b.impersonate 336 } 337 338 // AssertContainer checks if the token is valid within the given container. 339 // 340 // Note: cnr is assumed to refer to the issuer's container, otherwise the check 341 // is meaningless. 342 // 343 // Zero Token is valid in any container. 344 // 345 // See also SetEACLTable. 346 func (b Token) AssertContainer(cnr cid.ID) bool { 347 if !b.eaclTableSet { 348 return true 349 } 350 351 cnrTable, set := b.eaclTable.CID() 352 return !set || cnrTable.Equals(cnr) 353 } 354 355 // ForUser specifies ID of the user who can use the Token for the operations 356 // within issuer's container(s). 357 // 358 // Optional: by default, any user has access to Token usage. 359 // 360 // See also AssertUser. 361 func (b *Token) ForUser(id user.ID) { 362 b.targetUser = id 363 b.targetUserSet = true 364 } 365 366 // AssertUser checks if the Token is issued to the given user. 367 // 368 // Zero Token is available to any user. 369 // 370 // See also ForUser. 371 func (b Token) AssertUser(id user.ID) bool { 372 return !b.targetUserSet || b.targetUser.Equals(id) 373 } 374 375 // Sign calculates and writes signature of the Token data using issuer's secret. 376 // Returns signature calculation errors. 377 // 378 // Sign MUST be called if Token is going to be transmitted over 379 // FrostFS API V2 protocol. 380 // 381 // Note that any Token mutation is likely to break the signature, so it is 382 // expected to be calculated as a final stage of Token formation. 383 // 384 // See also VerifySignature, Issuer. 385 func (b *Token) Sign(key ecdsa.PrivateKey) error { 386 var sig frostfscrypto.Signature 387 388 err := sig.Calculate(frostfsecdsa.Signer(key), b.signedData()) 389 if err != nil { 390 return err 391 } 392 393 sig.WriteToV2(&b.sig) 394 b.sigSet = true 395 396 return nil 397 } 398 399 // VerifySignature checks if Token signature is presented and valid. 400 // 401 // Zero Token fails the check. 402 // 403 // See also Sign. 404 func (b Token) VerifySignature() bool { 405 if !b.sigSet { 406 return false 407 } 408 409 var sig frostfscrypto.Signature 410 411 // TODO: (#233) check owner<->key relation 412 return sig.ReadFromV2(b.sig) == nil && sig.Verify(b.signedData()) 413 } 414 415 // Marshal encodes Token into a binary format of the FrostFS API protocol 416 // (Protocol Buffers V3 with direct field order). 417 // 418 // See also Unmarshal. 419 func (b Token) Marshal() []byte { 420 var m acl.BearerToken 421 b.WriteToV2(&m) 422 423 return m.StableMarshal(nil) 424 } 425 426 // Unmarshal decodes FrostFS API protocol binary data into the Token 427 // (Protocol Buffers V3 with direct field order). Returns an error describing 428 // a format violation. 429 // 430 // See also Marshal. 431 func (b *Token) Unmarshal(data []byte) error { 432 var m acl.BearerToken 433 434 err := m.Unmarshal(data) 435 if err != nil { 436 return err 437 } 438 439 return b.readFromV2(m, false) 440 } 441 442 // MarshalJSON encodes Token into a JSON format of the FrostFS API protocol 443 // (Protocol Buffers V3 JSON). 444 // 445 // See also UnmarshalJSON. 446 func (b Token) MarshalJSON() ([]byte, error) { 447 var m acl.BearerToken 448 b.WriteToV2(&m) 449 450 return m.MarshalJSON() 451 } 452 453 // UnmarshalJSON decodes FrostFS API protocol JSON data into the Token 454 // (Protocol Buffers V3 JSON). Returns an error describing a format violation. 455 // 456 // See also MarshalJSON. 457 func (b *Token) UnmarshalJSON(data []byte) error { 458 var m acl.BearerToken 459 460 err := m.UnmarshalJSON(data) 461 if err != nil { 462 return err 463 } 464 465 return b.readFromV2(m, false) 466 } 467 468 // SigningKeyBytes returns issuer's public key in a binary format of 469 // FrostFS API protocol. 470 // 471 // Unsigned Token has empty key. 472 // 473 // See also ResolveIssuer. 474 func (b Token) SigningKeyBytes() []byte { 475 if b.sigSet { 476 return b.sig.GetKey() 477 } 478 479 return nil 480 } 481 482 // ResolveIssuer resolves issuer's user.ID from the key used for Token signing. 483 // Returns zero user.ID if Token is unsigned or key has incorrect format. 484 // 485 // See also SigningKeyBytes. 486 func ResolveIssuer(b Token) (usr user.ID) { 487 binKey := b.SigningKeyBytes() 488 489 if len(binKey) != 0 { 490 var key frostfsecdsa.PublicKey 491 if key.Decode(binKey) == nil { 492 user.IDFromKey(&usr, ecdsa.PublicKey(key)) 493 } 494 } 495 496 return 497 }