2021-06-14 13:39:25 +00:00
|
|
|
package tokens
|
2021-05-25 19:59:21 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2022-02-08 16:54:04 +00:00
|
|
|
"fmt"
|
2021-05-25 19:59:21 +00:00
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
2021-06-24 15:21:34 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2021-09-10 06:56:56 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/cache"
|
2021-05-25 19:59:21 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
2021-11-15 12:56:16 +00:00
|
|
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
2022-02-08 16:54:04 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/object/address"
|
2022-03-01 19:02:24 +00:00
|
|
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
2021-11-15 12:56:16 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
2021-05-25 19:59:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2021-05-26 18:23:36 +00:00
|
|
|
// Credentials is a bearer token get/put interface.
|
2021-05-25 19:59:21 +00:00
|
|
|
Credentials interface {
|
2022-02-08 16:54:04 +00:00
|
|
|
GetBox(context.Context, *address.Address) (*accessbox.Box, error)
|
|
|
|
Put(context.Context, *cid.ID, *owner.ID, *accessbox.AccessBox, uint64, ...*keys.PublicKey) (*address.Address, error)
|
2021-05-25 19:59:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cred struct {
|
2021-09-08 07:08:56 +00:00
|
|
|
key *keys.PrivateKey
|
2022-03-01 19:02:24 +00:00
|
|
|
neoFS NeoFS
|
2021-09-10 06:56:56 +00:00
|
|
|
cache *cache.AccessBoxCache
|
2021-05-25 19:59:21 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2022-03-01 19:02:24 +00:00
|
|
|
// PrmObjectCreate groups parameters of objects created by credential tool.
|
|
|
|
type PrmObjectCreate struct {
|
|
|
|
// NeoFS identifier of the object creator.
|
|
|
|
Creator owner.ID
|
|
|
|
|
|
|
|
// NeoFS container to store the object.
|
|
|
|
Container cid.ID
|
|
|
|
|
|
|
|
// Object creation time.
|
|
|
|
Time time.Time
|
|
|
|
|
|
|
|
// File name.
|
|
|
|
Filename string
|
|
|
|
|
|
|
|
// Last NeoFS epoch of the object lifetime.
|
|
|
|
ExpirationEpoch uint64
|
|
|
|
|
|
|
|
// Object payload.
|
|
|
|
Payload []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// NeoFS represents virtual connection to NeoFS network.
|
|
|
|
type NeoFS interface {
|
|
|
|
// CreateObject creates and saves a parameterized object in the specified
|
|
|
|
// NeoFS container from a specific user. Returns ID of the saved object.
|
|
|
|
//
|
|
|
|
// Returns exactly one non-nil value. Returns any error encountered which
|
|
|
|
// prevented the object to be created.
|
|
|
|
CreateObject(context.Context, PrmObjectCreate) (*oid.ID, error)
|
|
|
|
|
|
|
|
// ReadObjectPayload reads payload of the object from NeoFS network by address
|
|
|
|
// into memory.
|
|
|
|
//
|
|
|
|
// Returns exactly one non-nil value. Returns any error encountered which
|
|
|
|
// prevented the object payload to be read.
|
|
|
|
ReadObjectPayload(context.Context, address.Address) ([]byte, error)
|
|
|
|
}
|
|
|
|
|
2021-05-25 19:59:21 +00:00
|
|
|
var (
|
2021-05-26 18:23:36 +00:00
|
|
|
// ErrEmptyPublicKeys is returned when no HCS keys are provided.
|
|
|
|
ErrEmptyPublicKeys = errors.New("HCS public keys could not be empty")
|
|
|
|
// ErrEmptyBearerToken is returned when no bearer token is provided.
|
2021-05-25 19:59:21 +00:00
|
|
|
ErrEmptyBearerToken = errors.New("Bearer token could not be empty")
|
|
|
|
)
|
|
|
|
|
|
|
|
var _ = New
|
|
|
|
|
2021-05-26 18:23:36 +00:00
|
|
|
// New creates new Credentials instance using given cli and key.
|
2022-03-01 19:02:24 +00:00
|
|
|
func New(neoFS NeoFS, key *keys.PrivateKey, config *cache.Config) Credentials {
|
|
|
|
return &cred{neoFS: neoFS, key: key, cache: cache.NewAccessBoxCache(config)}
|
2021-05-25 19:59:21 +00:00
|
|
|
}
|
|
|
|
|
2022-02-08 16:54:04 +00:00
|
|
|
func (c *cred) GetBox(ctx context.Context, addr *address.Address) (*accessbox.Box, error) {
|
|
|
|
cachedBox := c.cache.Get(addr)
|
2021-09-08 07:08:56 +00:00
|
|
|
if cachedBox != nil {
|
|
|
|
return cachedBox, nil
|
|
|
|
}
|
|
|
|
|
2022-02-08 16:54:04 +00:00
|
|
|
box, err := c.getAccessBox(ctx, addr)
|
2021-06-14 13:39:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-08 07:08:56 +00:00
|
|
|
cachedBox, err = box.GetBox(c.key)
|
2021-07-16 12:35:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-02-08 16:54:04 +00:00
|
|
|
if err = c.cache.Put(addr, cachedBox); err != nil {
|
2021-09-08 07:08:56 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return cachedBox, nil
|
2021-07-16 12:35:07 +00:00
|
|
|
}
|
|
|
|
|
2022-02-08 16:54:04 +00:00
|
|
|
func (c *cred) getAccessBox(ctx context.Context, addr *address.Address) (*accessbox.AccessBox, error) {
|
2022-03-01 19:02:24 +00:00
|
|
|
data, err := c.neoFS.ReadObjectPayload(ctx, *addr)
|
2022-02-08 16:54:04 +00:00
|
|
|
if err != nil {
|
2022-03-01 19:02:24 +00:00
|
|
|
return nil, fmt.Errorf("read payload: %w", err)
|
2021-05-26 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-08 16:54:04 +00:00
|
|
|
// decode access box
|
|
|
|
var box accessbox.AccessBox
|
|
|
|
if err = box.Unmarshal(data); err != nil {
|
2021-05-25 19:59:21 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-08 16:54:04 +00:00
|
|
|
|
2021-06-14 13:39:25 +00:00
|
|
|
return &box, nil
|
2021-05-25 19:59:21 +00:00
|
|
|
}
|
|
|
|
|
2022-03-01 19:02:24 +00:00
|
|
|
func (c *cred) Put(ctx context.Context, idCnr *cid.ID, issuer *owner.ID, box *accessbox.AccessBox, expiration uint64, keys ...*keys.PublicKey) (*address.Address, error) {
|
2021-05-25 19:59:21 +00:00
|
|
|
var (
|
2021-06-15 15:48:30 +00:00
|
|
|
err error
|
2022-03-01 19:02:24 +00:00
|
|
|
created = time.Now()
|
2021-05-25 19:59:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
if len(keys) == 0 {
|
|
|
|
return nil, ErrEmptyPublicKeys
|
2021-06-14 13:39:25 +00:00
|
|
|
} else if box == nil {
|
2021-05-25 19:59:21 +00:00
|
|
|
return nil, ErrEmptyBearerToken
|
2021-06-14 13:39:25 +00:00
|
|
|
}
|
|
|
|
data, err := box.Marshal()
|
|
|
|
if err != nil {
|
2021-05-25 19:59:21 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-03-01 19:02:24 +00:00
|
|
|
idObj, err := c.neoFS.CreateObject(ctx, PrmObjectCreate{
|
|
|
|
Creator: *issuer,
|
|
|
|
Container: *idCnr,
|
|
|
|
Time: created,
|
|
|
|
Filename: strconv.FormatInt(created.Unix(), 10) + "_access.box",
|
|
|
|
ExpirationEpoch: expiration,
|
|
|
|
Payload: data,
|
|
|
|
})
|
2021-05-26 16:48:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-08 16:54:04 +00:00
|
|
|
|
|
|
|
addr := address.NewAddress()
|
2022-03-01 19:02:24 +00:00
|
|
|
addr.SetObjectID(idObj)
|
|
|
|
addr.SetContainerID(idCnr)
|
2022-02-08 16:54:04 +00:00
|
|
|
return addr, nil
|
2021-05-25 19:59:21 +00:00
|
|
|
}
|