[#131] authmate: Add agent.UpdateSecret
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
dea7b39805
commit
f74ab12f91
1 changed files with 141 additions and 19 deletions
|
@ -11,6 +11,8 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
sessionv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/cache"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
|
||||
|
@ -108,6 +110,21 @@ type (
|
|||
UpdateCreds *UpdateOptions
|
||||
}
|
||||
|
||||
// UpdateSecretOptions contains options for passing to Agent.UpdateSecret method.
|
||||
UpdateSecretOptions struct {
|
||||
FrostFSKey *keys.PrivateKey
|
||||
GatesPublicKeys []*keys.PublicKey
|
||||
Address oid.Address
|
||||
GatePrivateKey *keys.PrivateKey
|
||||
}
|
||||
|
||||
tokenUpdateOptions struct {
|
||||
frostFSKey *keys.PrivateKey
|
||||
gatesPublicKeys []*keys.PublicKey
|
||||
lifetime lifetimeOptions
|
||||
box *accessbox.Box
|
||||
}
|
||||
|
||||
// ContainerOptions groups parameters of auth container to put the secret into.
|
||||
ContainerOptions struct {
|
||||
ID cid.ID
|
||||
|
@ -136,8 +153,8 @@ type lifetimeOptions struct {
|
|||
|
||||
type (
|
||||
issuingResult struct {
|
||||
AccessKeyID string `json:"access_key_id"`
|
||||
InitialAccessKeyID string `json:"initial_access_key_id"`
|
||||
AccessKeyID string `json:"access_key_id"`
|
||||
SecretAccessKey string `json:"secret_access_key"`
|
||||
OwnerPrivateKey string `json:"owner_private_key"`
|
||||
WalletPublicKey string `json:"wallet_public_key"`
|
||||
|
@ -237,12 +254,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
|||
return fmt.Errorf("create tokens: %w", err)
|
||||
}
|
||||
|
||||
var secret []byte
|
||||
if options.UpdateCreds != nil {
|
||||
secret = options.UpdateCreds.SecretAccessKey
|
||||
}
|
||||
|
||||
box, secrets, err := accessbox.PackTokens(gatesData, secret)
|
||||
box, secrets, err := accessbox.PackTokens(gatesData, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pack tokens: %w", err)
|
||||
}
|
||||
|
@ -261,24 +273,15 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
|||
|
||||
creds := tokens.New(a.frostFS, secrets.EphemeralKey, cache.DefaultAccessBoxConfig(a.log))
|
||||
|
||||
var addr oid.Address
|
||||
var oldAddr oid.Address
|
||||
if options.UpdateCreds != nil {
|
||||
oldAddr = options.UpdateCreds.Address
|
||||
addr, err = creds.Update(ctx, oldAddr, idOwner, box, lifetime.Exp, options.GatesPublicKeys...)
|
||||
} else {
|
||||
addr, err = creds.Put(ctx, id, idOwner, box, lifetime.Exp, options.GatesPublicKeys...)
|
||||
oldAddr = addr
|
||||
}
|
||||
addr, err := creds.Put(ctx, id, idOwner, box, lifetime.Exp, options.GatesPublicKeys...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to put creds: %w", err)
|
||||
}
|
||||
|
||||
accessKeyID := addr.Container().EncodeToString() + "0" + addr.Object().EncodeToString()
|
||||
|
||||
accessKeyID := accessKeyIDFromAddr(addr)
|
||||
ir := &issuingResult{
|
||||
InitialAccessKeyID: accessKeyID,
|
||||
AccessKeyID: accessKeyID,
|
||||
InitialAccessKeyID: oldAddr.Container().EncodeToString() + "0" + oldAddr.Object().EncodeToString(),
|
||||
SecretAccessKey: secrets.AccessKey,
|
||||
OwnerPrivateKey: hex.EncodeToString(secrets.EphemeralKey.Bytes()),
|
||||
WalletPublicKey: hex.EncodeToString(options.FrostFSKey.PublicKey().Bytes()),
|
||||
|
@ -309,6 +312,73 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateSecret updates an auth token (change list of gates that can use credential), puts new cred version to the FrostFS network and writes to io.Writer a result.
|
||||
func (a *Agent) UpdateSecret(ctx context.Context, w io.Writer, options *UpdateSecretOptions) error {
|
||||
creds := tokens.New(a.frostFS, options.GatePrivateKey, cache.DefaultAccessBoxConfig(a.log))
|
||||
|
||||
box, err := creds.GetBox(ctx, options.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get accessbox: %w", err)
|
||||
}
|
||||
|
||||
secret, err := hex.DecodeString(box.Gate.AccessKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode secret key access box: %w", err)
|
||||
}
|
||||
|
||||
lifetime := getLifetimeFromGateData(box.Gate)
|
||||
tokenOptions := tokenUpdateOptions{
|
||||
frostFSKey: options.FrostFSKey,
|
||||
gatesPublicKeys: options.GatesPublicKeys,
|
||||
lifetime: lifetime,
|
||||
box: box,
|
||||
}
|
||||
|
||||
gatesData, err := formTokensToUpdate(tokenOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create tokens: %w", err)
|
||||
}
|
||||
|
||||
updatedBox, secrets, err := accessbox.PackTokens(gatesData, secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pack tokens: %w", err)
|
||||
}
|
||||
|
||||
var idOwner user.ID
|
||||
user.IDFromKey(&idOwner, options.FrostFSKey.PrivateKey.PublicKey)
|
||||
a.log.Info("update access cred object into FrostFS",
|
||||
zap.Stringer("owner_tkn", idOwner))
|
||||
|
||||
oldAddr := options.Address
|
||||
addr, err := creds.Update(ctx, oldAddr, idOwner, updatedBox, lifetime.Exp, options.GatesPublicKeys...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update creds: %w", err)
|
||||
}
|
||||
|
||||
ir := &issuingResult{
|
||||
AccessKeyID: accessKeyIDFromAddr(addr),
|
||||
InitialAccessKeyID: accessKeyIDFromAddr(oldAddr),
|
||||
SecretAccessKey: secrets.AccessKey,
|
||||
OwnerPrivateKey: hex.EncodeToString(secrets.EphemeralKey.Bytes()),
|
||||
WalletPublicKey: hex.EncodeToString(options.FrostFSKey.PublicKey().Bytes()),
|
||||
ContainerID: addr.Container().EncodeToString(),
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(ir)
|
||||
}
|
||||
|
||||
func getLifetimeFromGateData(gateData *accessbox.GateData) lifetimeOptions {
|
||||
var btokenv2 acl.BearerToken
|
||||
gateData.BearerToken.WriteToV2(&btokenv2)
|
||||
|
||||
return lifetimeOptions{
|
||||
Iat: btokenv2.GetBody().GetLifetime().GetIat(),
|
||||
Exp: btokenv2.GetBody().GetLifetime().GetExp(),
|
||||
}
|
||||
}
|
||||
|
||||
// ObtainSecret receives an existing secret access key from FrostFS and
|
||||
// writes to io.Writer the secret access key.
|
||||
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
|
||||
|
@ -467,3 +537,55 @@ func createTokens(options *IssueSecretOptions, lifetime lifetimeOptions) ([]*acc
|
|||
|
||||
return gates, nil
|
||||
}
|
||||
|
||||
func formTokensToUpdate(options tokenUpdateOptions) ([]*accessbox.GateData, error) {
|
||||
btoken := options.box.Gate.BearerToken
|
||||
table := btoken.EACLTable()
|
||||
|
||||
bearerTokens, err := buildBearerTokens(options.frostFSKey, btoken.Impersonate(), &table, options.lifetime, options.gatesPublicKeys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build bearer tokens: %w", err)
|
||||
}
|
||||
|
||||
gates := make([]*accessbox.GateData, len(options.gatesPublicKeys))
|
||||
for i, gateKey := range options.gatesPublicKeys {
|
||||
gates[i] = accessbox.NewGateData(gateKey, bearerTokens[i])
|
||||
}
|
||||
|
||||
sessionRules := make([]sessionTokenContext, len(options.box.Gate.SessionTokens))
|
||||
for i, token := range options.box.Gate.SessionTokens {
|
||||
var stoken sessionv2.Token
|
||||
token.WriteToV2(&stoken)
|
||||
|
||||
sessionCtx, ok := stoken.GetBody().GetContext().(*sessionv2.ContainerSessionContext)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("get context from session token: %w", err)
|
||||
}
|
||||
|
||||
var cnrID cid.ID
|
||||
if cnrIDv2 := sessionCtx.ContainerID(); cnrIDv2 != nil {
|
||||
if err = cnrID.ReadFromV2(*cnrIDv2); err != nil {
|
||||
return nil, fmt.Errorf("read from v2 container id: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
sessionRules[i] = sessionTokenContext{
|
||||
verb: session.ContainerVerb(sessionCtx.Verb()),
|
||||
containerID: cnrID,
|
||||
}
|
||||
}
|
||||
|
||||
sessionTokens, err := buildSessionTokens(options.frostFSKey, options.lifetime, sessionRules, options.gatesPublicKeys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to biuild session token: %w", err)
|
||||
}
|
||||
for i, sessionTkns := range sessionTokens {
|
||||
gates[i].SessionTokens = sessionTkns
|
||||
}
|
||||
|
||||
return gates, nil
|
||||
}
|
||||
|
||||
func accessKeyIDFromAddr(addr oid.Address) string {
|
||||
return addr.Container().EncodeToString() + "0" + addr.Object().EncodeToString()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue