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  }