2021-05-19 09:28:17 +00:00
|
|
|
package authmate
|
2021-05-18 18:49:09 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/ecdsa"
|
2021-05-25 16:52:29 +00:00
|
|
|
"encoding/hex"
|
2021-05-18 18:49:09 +00:00
|
|
|
"encoding/json"
|
2021-05-20 10:14:17 +00:00
|
|
|
"fmt"
|
2021-05-18 18:49:09 +00:00
|
|
|
"io"
|
2021-09-01 11:30:15 +00:00
|
|
|
"os"
|
2021-05-18 18:49:09 +00:00
|
|
|
"time"
|
|
|
|
|
2021-06-16 20:09:51 +00:00
|
|
|
"github.com/google/uuid"
|
2021-06-24 15:21:34 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2022-04-28 15:12:57 +00:00
|
|
|
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
2021-09-10 06:56:56 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/cache"
|
2021-06-14 13:39:25 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
|
2022-04-25 09:57:58 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
2021-11-15 12:56:16 +00:00
|
|
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
2022-02-08 16:54:04 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/object/address"
|
2021-08-27 12:48:59 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/policy"
|
2021-11-15 12:56:16 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
2022-04-25 09:57:58 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
2021-05-18 18:49:09 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
2022-03-01 19:02:24 +00:00
|
|
|
// PrmContainerCreate groups parameters of containers created by authmate.
|
|
|
|
type PrmContainerCreate struct {
|
|
|
|
// NeoFS identifier of the container creator.
|
2022-04-25 09:57:58 +00:00
|
|
|
Owner user.ID
|
2022-03-01 19:02:24 +00:00
|
|
|
|
|
|
|
// Container placement policy.
|
|
|
|
Policy netmap.PlacementPolicy
|
|
|
|
|
|
|
|
// Friendly name for the container (optional).
|
|
|
|
FriendlyName string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NetworkState represents NeoFS network state which is needed for authmate processing.
|
|
|
|
type NetworkState struct {
|
|
|
|
// Current NeoFS time.
|
|
|
|
Epoch uint64
|
|
|
|
// Duration of the Morph chain block in ms.
|
|
|
|
BlockDuration int64
|
|
|
|
// Duration of the NeoFS epoch in Morph chain blocks.
|
|
|
|
EpochDuration uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// NeoFS represents virtual connection to NeoFS network.
|
|
|
|
type NeoFS interface {
|
|
|
|
// NeoFS interface required by credential tool.
|
|
|
|
tokens.NeoFS
|
|
|
|
|
|
|
|
// ContainerExists checks container presence in NeoFS by identifier.
|
2022-04-13 16:56:58 +00:00
|
|
|
// Returns nil if container exists.
|
2022-03-01 19:02:24 +00:00
|
|
|
ContainerExists(context.Context, cid.ID) error
|
|
|
|
|
|
|
|
// CreateContainer creates and saves parameterized container in NeoFS.
|
2022-04-13 16:56:58 +00:00
|
|
|
// It sets 'Timestamp' attribute to the current time.
|
|
|
|
// It returns the ID of the saved container.
|
2022-03-01 19:02:24 +00:00
|
|
|
//
|
2022-04-13 16:56:58 +00:00
|
|
|
// The container must be private with GET access for OTHERS group.
|
2022-03-01 19:02:24 +00:00
|
|
|
// Creation time should also be stamped.
|
|
|
|
//
|
2022-04-13 16:56:58 +00:00
|
|
|
// It returns exactly one non-nil value. It returns any error encountered which
|
|
|
|
// prevented the container from being created.
|
2022-03-01 19:02:24 +00:00
|
|
|
CreateContainer(context.Context, PrmContainerCreate) (*cid.ID, error)
|
|
|
|
|
2022-04-13 16:56:58 +00:00
|
|
|
// TimeToEpoch computes the current epoch and the epoch that corresponds to the provided time.
|
2022-03-05 08:53:01 +00:00
|
|
|
// Note:
|
|
|
|
// * time must be in the future
|
|
|
|
// * time will be ceil rounded to match epoch
|
2022-03-01 19:02:24 +00:00
|
|
|
//
|
2022-04-13 16:56:58 +00:00
|
|
|
// It returns any error encountered which prevented computing epochs.
|
2022-03-05 08:53:01 +00:00
|
|
|
TimeToEpoch(context.Context, time.Time) (uint64, uint64, error)
|
2022-03-01 19:02:24 +00:00
|
|
|
}
|
2021-05-18 18:49:09 +00:00
|
|
|
|
2021-05-19 16:27:02 +00:00
|
|
|
// Agent contains client communicating with NeoFS and logger.
|
2021-05-18 18:49:09 +00:00
|
|
|
type Agent struct {
|
2022-03-01 19:02:24 +00:00
|
|
|
neoFS NeoFS
|
|
|
|
log *zap.Logger
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2021-05-19 16:27:02 +00:00
|
|
|
// New creates an object of type Agent that consists of Client and logger.
|
2022-03-01 19:02:24 +00:00
|
|
|
func New(log *zap.Logger, neoFS NeoFS) *Agent {
|
|
|
|
return &Agent{log: log, neoFS: neoFS}
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type (
|
2021-07-16 12:35:07 +00:00
|
|
|
// ContainerPolicies contains mapping of aws LocationConstraint to neofs PlacementPolicy.
|
|
|
|
ContainerPolicies map[string]string
|
|
|
|
|
2021-05-19 16:27:02 +00:00
|
|
|
// IssueSecretOptions contains options for passing to Agent.IssueSecret method.
|
2021-05-18 18:49:09 +00:00
|
|
|
IssueSecretOptions struct {
|
2022-03-03 12:43:56 +00:00
|
|
|
Container ContainerOptions
|
2021-06-24 15:21:34 +00:00
|
|
|
NeoFSKey *keys.PrivateKey
|
|
|
|
GatesPublicKeys []*keys.PublicKey
|
2021-05-18 18:49:09 +00:00
|
|
|
EACLRules []byte
|
2022-01-31 18:40:00 +00:00
|
|
|
SessionTokenRules []byte
|
2022-03-30 12:23:00 +00:00
|
|
|
SkipSessionRules bool
|
2021-10-26 12:03:51 +00:00
|
|
|
Lifetime time.Duration
|
2021-09-01 11:30:15 +00:00
|
|
|
AwsCliCredentialsFile string
|
2021-07-16 12:35:07 +00:00
|
|
|
ContainerPolicies ContainerPolicies
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2022-03-03 12:43:56 +00:00
|
|
|
// ContainerOptions groups parameters of auth container to put the secret into.
|
|
|
|
ContainerOptions struct {
|
|
|
|
ID *cid.ID
|
|
|
|
FriendlyName string
|
|
|
|
PlacementPolicy string
|
|
|
|
}
|
|
|
|
|
2021-05-19 16:27:02 +00:00
|
|
|
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
|
2021-05-18 18:49:09 +00:00
|
|
|
ObtainSecretOptions struct {
|
|
|
|
SecretAddress string
|
2021-06-24 15:21:34 +00:00
|
|
|
GatePrivateKey *keys.PrivateKey
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2022-04-13 16:56:58 +00:00
|
|
|
// lifetimeOptions holds NeoFS epochs, iat -- epoch which the token was issued at, exp -- epoch when the token expires.
|
2021-06-28 13:20:11 +00:00
|
|
|
type lifetimeOptions struct {
|
|
|
|
Iat uint64
|
|
|
|
Exp uint64
|
|
|
|
}
|
|
|
|
|
2021-05-18 18:49:09 +00:00
|
|
|
type (
|
|
|
|
issuingResult struct {
|
|
|
|
AccessKeyID string `json:"access_key_id"`
|
|
|
|
SecretAccessKey string `json:"secret_access_key"`
|
|
|
|
OwnerPrivateKey string `json:"owner_private_key"`
|
2021-07-16 12:08:05 +00:00
|
|
|
ContainerID string `json:"container_id"`
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
obtainingResult struct {
|
2022-04-25 09:57:58 +00:00
|
|
|
BearerToken *bearer.Token `json:"-"`
|
|
|
|
SecretAccessKey string `json:"secret_access_key"`
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
func (a *Agent) checkContainer(ctx context.Context, opts ContainerOptions, idOwner user.ID) (*cid.ID, error) {
|
2022-03-03 12:43:56 +00:00
|
|
|
if opts.ID != nil {
|
2022-04-13 16:56:58 +00:00
|
|
|
// check that the container exists
|
2022-03-01 19:02:24 +00:00
|
|
|
return opts.ID, a.neoFS.ContainerExists(ctx, *opts.ID)
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2022-03-03 12:43:56 +00:00
|
|
|
pp, err := policy.Parse(opts.PlacementPolicy)
|
2021-05-18 18:49:09 +00:00
|
|
|
if err != nil {
|
2021-05-20 10:14:17 +00:00
|
|
|
return nil, fmt.Errorf("failed to build placement policy: %w", err)
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2022-03-01 19:02:24 +00:00
|
|
|
cnrID, err := a.neoFS.CreateContainer(ctx, PrmContainerCreate{
|
2022-04-25 09:57:58 +00:00
|
|
|
Owner: idOwner,
|
2022-03-01 19:02:24 +00:00
|
|
|
Policy: *pp,
|
|
|
|
FriendlyName: opts.FriendlyName,
|
|
|
|
})
|
2021-05-26 16:48:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-03-03 12:43:56 +00:00
|
|
|
return cnrID, nil
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 12:35:07 +00:00
|
|
|
func checkPolicy(policyString string) (*netmap.PlacementPolicy, error) {
|
|
|
|
result, err := policy.Parse(policyString)
|
|
|
|
if err == nil {
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
result = netmap.NewPlacementPolicy()
|
|
|
|
if err = result.UnmarshalJSON([]byte(policyString)); err == nil {
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("can't parse placement policy")
|
|
|
|
}
|
|
|
|
|
|
|
|
func preparePolicy(policy ContainerPolicies) ([]*accessbox.AccessBox_ContainerPolicy, error) {
|
|
|
|
if policy == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var result []*accessbox.AccessBox_ContainerPolicy
|
|
|
|
for locationConstraint, placementPolicy := range policy {
|
|
|
|
parsedPolicy, err := checkPolicy(placementPolicy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
marshaled, err := parsedPolicy.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("can't marshal placement policy: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
result = append(result, &accessbox.AccessBox_ContainerPolicy{
|
|
|
|
LocationConstraint: locationConstraint,
|
|
|
|
Policy: marshaled,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2021-05-19 16:27:02 +00:00
|
|
|
// IssueSecret creates an auth token, puts it in the NeoFS network and writes to io.Writer a new secret access key.
|
2021-05-18 18:49:09 +00:00
|
|
|
func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecretOptions) error {
|
|
|
|
var (
|
2021-06-28 13:20:11 +00:00
|
|
|
err error
|
2022-02-08 16:54:04 +00:00
|
|
|
id *cid.ID
|
2021-06-28 13:20:11 +00:00
|
|
|
box *accessbox.AccessBox
|
|
|
|
lifetime lifetimeOptions
|
2021-05-18 18:49:09 +00:00
|
|
|
)
|
|
|
|
|
2021-07-16 12:35:07 +00:00
|
|
|
policies, err := preparePolicy(options.ContainerPolicies)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-03-05 08:53:01 +00:00
|
|
|
lifetime.Iat, lifetime.Exp, err = a.neoFS.TimeToEpoch(ctx, time.Now().Add(options.Lifetime))
|
2021-06-28 13:20:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-03-11 09:59:52 +00:00
|
|
|
gatesData, err := createTokens(options, lifetime)
|
2021-05-18 18:49:09 +00:00
|
|
|
if err != nil {
|
2022-02-14 15:07:50 +00:00
|
|
|
return err
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-18 15:15:58 +00:00
|
|
|
box, secrets, err := accessbox.PackTokens(gatesData)
|
2021-06-14 13:39:25 +00:00
|
|
|
if err != nil {
|
2021-06-18 15:15:58 +00:00
|
|
|
return err
|
2021-06-14 13:39:25 +00:00
|
|
|
}
|
2021-06-16 20:09:51 +00:00
|
|
|
|
2021-07-16 12:35:07 +00:00
|
|
|
box.ContainerPolicy = policies
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
var idOwner user.ID
|
|
|
|
user.IDFromKey(&idOwner, options.NeoFSKey.PrivateKey.PublicKey)
|
2022-03-11 09:59:52 +00:00
|
|
|
|
|
|
|
a.log.Info("check container or create", zap.Stringer("cid", options.Container.ID),
|
|
|
|
zap.String("friendly_name", options.Container.FriendlyName),
|
|
|
|
zap.String("placement_policy", options.Container.PlacementPolicy))
|
|
|
|
if id, err = a.checkContainer(ctx, options.Container, idOwner); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-06-16 14:07:31 +00:00
|
|
|
a.log.Info("store bearer token into NeoFS",
|
2022-02-08 16:54:04 +00:00
|
|
|
zap.Stringer("owner_tkn", idOwner))
|
2021-06-16 14:07:31 +00:00
|
|
|
|
2022-02-08 16:54:04 +00:00
|
|
|
addr, err := tokens.
|
2022-03-01 19:02:24 +00:00
|
|
|
New(a.neoFS, secrets.EphemeralKey, cache.DefaultAccessBoxConfig()).
|
2022-02-08 16:54:04 +00:00
|
|
|
Put(ctx, id, idOwner, box, lifetime.Exp, options.GatesPublicKeys...)
|
2021-05-18 18:49:09 +00:00
|
|
|
if err != nil {
|
2021-05-20 10:14:17 +00:00
|
|
|
return fmt.Errorf("failed to put bearer token: %w", err)
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
cnrID, _ := addr.ContainerID()
|
|
|
|
objID, _ := addr.ObjectID()
|
|
|
|
accessKeyID := cnrID.EncodeToString() + "0" + objID.EncodeToString()
|
2021-06-02 18:53:20 +00:00
|
|
|
|
2021-05-18 18:49:09 +00:00
|
|
|
ir := &issuingResult{
|
2021-06-02 18:53:20 +00:00
|
|
|
AccessKeyID: accessKeyID,
|
2021-06-17 16:45:50 +00:00
|
|
|
SecretAccessKey: secrets.AccessKey,
|
2021-06-24 15:21:34 +00:00
|
|
|
OwnerPrivateKey: hex.EncodeToString(secrets.EphemeralKey.Bytes()),
|
2022-02-08 16:54:04 +00:00
|
|
|
ContainerID: id.String(),
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
enc := json.NewEncoder(w)
|
|
|
|
enc.SetIndent("", " ")
|
2021-09-01 11:30:15 +00:00
|
|
|
if err = enc.Encode(ir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if options.AwsCliCredentialsFile != "" {
|
2022-04-25 09:57:58 +00:00
|
|
|
profileName := "authmate_cred_" + objID.EncodeToString()
|
2021-09-01 11:30:15 +00:00
|
|
|
if _, err = os.Stat(options.AwsCliCredentialsFile); os.IsNotExist(err) {
|
|
|
|
profileName = "default"
|
|
|
|
}
|
|
|
|
file, err := os.OpenFile(options.AwsCliCredentialsFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("couldn't open aws cli credentials file: %w", err)
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
if _, err = file.WriteString(fmt.Sprintf("\n[%s]\naws_access_key_id = %s\naws_secret_access_key = %s\n",
|
|
|
|
profileName, accessKeyID, secrets.AccessKey)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2021-05-19 16:27:02 +00:00
|
|
|
// ObtainSecret receives an existing secret access key from NeoFS and
|
|
|
|
// writes to io.Writer the secret access key.
|
2021-05-18 18:49:09 +00:00
|
|
|
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
|
2022-03-01 19:02:24 +00:00
|
|
|
bearerCreds := tokens.New(a.neoFS, options.GatePrivateKey, cache.DefaultAccessBoxConfig())
|
2022-02-08 16:54:04 +00:00
|
|
|
addr := address.NewAddress()
|
|
|
|
if err := addr.Parse(options.SecretAddress); err != nil {
|
2021-05-20 10:14:17 +00:00
|
|
|
return fmt.Errorf("failed to parse secret address: %w", err)
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2022-02-08 16:54:04 +00:00
|
|
|
box, err := bearerCreds.GetBox(ctx, addr)
|
2021-05-18 18:49:09 +00:00
|
|
|
if err != nil {
|
2021-06-17 16:45:50 +00:00
|
|
|
return fmt.Errorf("failed to get tokens: %w", err)
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
or := &obtainingResult{
|
2021-07-16 12:35:07 +00:00
|
|
|
BearerToken: box.Gate.BearerToken,
|
|
|
|
SecretAccessKey: box.Gate.AccessKey,
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
enc := json.NewEncoder(w)
|
|
|
|
enc.SetIndent("", " ")
|
|
|
|
return enc.Encode(or)
|
|
|
|
}
|
|
|
|
|
2022-03-11 09:59:52 +00:00
|
|
|
func buildEACLTable(eaclTable []byte) (*eacl.Table, error) {
|
2021-05-18 18:49:09 +00:00
|
|
|
if len(eaclTable) != 0 {
|
2022-04-28 15:12:57 +00:00
|
|
|
// fixme(neofs-sdk-go/#235)
|
|
|
|
// Can't parse SDK version of eACL table because it requires
|
|
|
|
// non-empty container ID. Possible solution: read json of bearer
|
|
|
|
// token instead of eACL table.
|
|
|
|
v2table := new(v2acl.Table)
|
|
|
|
err := v2table.UnmarshalJSON(eaclTable)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return eacl.NewTableFromV2(v2table), nil
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
|
|
|
|
2022-04-28 15:12:57 +00:00
|
|
|
table := eacl.NewTable()
|
2021-05-18 18:49:09 +00:00
|
|
|
record := eacl.NewRecord()
|
|
|
|
record.SetOperation(eacl.OperationGet)
|
|
|
|
record.SetAction(eacl.ActionAllow)
|
|
|
|
eacl.AddFormedTarget(record, eacl.RoleOthers)
|
|
|
|
table.AddRecord(record)
|
|
|
|
|
2022-04-26 14:35:12 +00:00
|
|
|
for _, rec := range restrictedRecords() {
|
|
|
|
table.AddRecord(rec)
|
|
|
|
}
|
|
|
|
|
2021-05-18 18:49:09 +00:00
|
|
|
return table, nil
|
|
|
|
}
|
|
|
|
|
2022-04-26 14:35:12 +00:00
|
|
|
func restrictedRecords() (records []*eacl.Record) {
|
|
|
|
for op := eacl.OperationGet; op <= eacl.OperationRangeHash; op++ {
|
|
|
|
record := eacl.NewRecord()
|
|
|
|
record.SetOperation(op)
|
|
|
|
record.SetAction(eacl.ActionDeny)
|
|
|
|
eacl.AddFormedTarget(record, eacl.RoleOthers)
|
|
|
|
records = append(records, record)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-26 06:57:11 +00:00
|
|
|
func buildContext(rules []byte) ([]*session.ContainerContext, error) {
|
|
|
|
var sessionCtxs []*session.ContainerContext
|
2021-06-16 20:09:51 +00:00
|
|
|
|
|
|
|
if len(rules) != 0 {
|
|
|
|
// cast ToV2 temporary, because there is no method for unmarshalling in ContainerContext in api-go
|
2022-01-26 06:57:11 +00:00
|
|
|
err := json.Unmarshal(rules, &sessionCtxs)
|
2021-06-16 20:09:51 +00:00
|
|
|
if err != nil {
|
2022-01-26 06:57:11 +00:00
|
|
|
return nil, fmt.Errorf("failed to unmarshal rules for session token: %w", err)
|
2021-06-16 20:09:51 +00:00
|
|
|
}
|
2022-02-14 19:37:50 +00:00
|
|
|
|
|
|
|
var (
|
|
|
|
containsPut = false
|
|
|
|
containsSetEACL = false
|
|
|
|
)
|
|
|
|
for _, s := range sessionCtxs {
|
|
|
|
if s.IsForPut() {
|
|
|
|
containsPut = true
|
|
|
|
} else if s.IsForSetEACL() {
|
|
|
|
containsSetEACL = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if containsPut && !containsSetEACL {
|
|
|
|
ectx := session.NewContainerContext()
|
|
|
|
ectx.ForSetEACL()
|
|
|
|
sessionCtxs = append(sessionCtxs, ectx)
|
|
|
|
}
|
|
|
|
|
2022-01-26 06:57:11 +00:00
|
|
|
return sessionCtxs, nil
|
2021-05-28 20:48:23 +00:00
|
|
|
}
|
2022-01-26 06:57:11 +00:00
|
|
|
|
2022-01-31 10:50:41 +00:00
|
|
|
sessionCtxPut := session.NewContainerContext()
|
|
|
|
sessionCtxPut.ForPut()
|
|
|
|
|
|
|
|
sessionCtxDelete := session.NewContainerContext()
|
|
|
|
sessionCtxDelete.ForDelete()
|
|
|
|
|
|
|
|
sessionCtxEACL := session.NewContainerContext()
|
|
|
|
sessionCtxEACL.ForSetEACL()
|
|
|
|
|
|
|
|
return []*session.ContainerContext{sessionCtxPut, sessionCtxDelete, sessionCtxEACL}, nil
|
2021-06-16 20:09:51 +00:00
|
|
|
}
|
2021-05-28 20:48:23 +00:00
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
func buildBearerToken(key *keys.PrivateKey, table *eacl.Table, lifetime lifetimeOptions, gateKey *keys.PublicKey) (*bearer.Token, error) {
|
|
|
|
var ownerID user.ID
|
|
|
|
user.IDFromKey(&ownerID, (ecdsa.PublicKey)(*gateKey))
|
2021-06-16 14:07:31 +00:00
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
var bearerToken bearer.Token
|
|
|
|
bearerToken.SetEACLTable(*table)
|
|
|
|
bearerToken.SetOwnerID(ownerID)
|
|
|
|
bearerToken.SetExpiration(lifetime.Exp)
|
|
|
|
bearerToken.SetIssuedAt(lifetime.Iat)
|
|
|
|
bearerToken.SetNotBefore(lifetime.Iat)
|
2021-05-18 18:49:09 +00:00
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
return &bearerToken, bearerToken.Sign(key.PrivateKey)
|
2021-05-18 18:49:09 +00:00
|
|
|
}
|
2021-05-25 16:52:29 +00:00
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
func buildBearerTokens(key *keys.PrivateKey, table *eacl.Table, lifetime lifetimeOptions, gatesKeys []*keys.PublicKey) ([]*bearer.Token, error) {
|
|
|
|
bearerTokens := make([]*bearer.Token, 0, len(gatesKeys))
|
2021-06-18 15:15:58 +00:00
|
|
|
for _, gateKey := range gatesKeys {
|
2021-06-28 13:20:11 +00:00
|
|
|
tkn, err := buildBearerToken(key, table, lifetime, gateKey)
|
2021-06-17 16:45:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-06-18 15:15:58 +00:00
|
|
|
bearerTokens = append(bearerTokens, tkn)
|
2021-06-17 16:45:50 +00:00
|
|
|
}
|
2021-06-18 15:15:58 +00:00
|
|
|
return bearerTokens, nil
|
2021-06-17 16:45:50 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
func buildSessionToken(key *keys.PrivateKey, oid *user.ID, lifetime lifetimeOptions, ctx *session.ContainerContext, gateKey *keys.PublicKey) (*session.Token, error) {
|
2021-06-16 20:09:51 +00:00
|
|
|
tok := session.NewToken()
|
|
|
|
tok.SetContext(ctx)
|
|
|
|
uid, err := uuid.New().MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
tok.SetID(uid)
|
|
|
|
tok.SetOwnerID(oid)
|
2021-06-24 15:21:34 +00:00
|
|
|
tok.SetSessionKey(gateKey.Bytes())
|
2021-06-16 20:09:51 +00:00
|
|
|
|
2021-06-28 13:20:11 +00:00
|
|
|
tok.SetIat(lifetime.Iat)
|
|
|
|
tok.SetNbf(lifetime.Iat)
|
|
|
|
tok.SetExp(lifetime.Exp)
|
|
|
|
|
2021-06-24 15:21:34 +00:00
|
|
|
return tok, tok.Sign(&key.PrivateKey)
|
2021-06-16 20:09:51 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
func buildSessionTokens(key *keys.PrivateKey, oid *user.ID, lifetime lifetimeOptions, ctxs []*session.ContainerContext, gatesKeys []*keys.PublicKey) ([][]*session.Token, error) {
|
2022-01-26 06:57:11 +00:00
|
|
|
sessionTokens := make([][]*session.Token, 0, len(gatesKeys))
|
2021-06-18 15:15:58 +00:00
|
|
|
for _, gateKey := range gatesKeys {
|
2022-01-26 06:57:11 +00:00
|
|
|
tkns := make([]*session.Token, len(ctxs))
|
|
|
|
for i, ctx := range ctxs {
|
|
|
|
tkn, err := buildSessionToken(key, oid, lifetime, ctx, gateKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
tkns[i] = tkn
|
2021-06-18 15:15:58 +00:00
|
|
|
}
|
2022-01-26 06:57:11 +00:00
|
|
|
sessionTokens = append(sessionTokens, tkns)
|
2021-06-18 15:15:58 +00:00
|
|
|
}
|
|
|
|
return sessionTokens, nil
|
|
|
|
}
|
|
|
|
|
2022-03-11 09:59:52 +00:00
|
|
|
func createTokens(options *IssueSecretOptions, lifetime lifetimeOptions) ([]*accessbox.GateData, error) {
|
2021-06-18 15:15:58 +00:00
|
|
|
gates := make([]*accessbox.GateData, len(options.GatesPublicKeys))
|
|
|
|
|
2022-03-11 09:59:52 +00:00
|
|
|
table, err := buildEACLTable(options.EACLRules)
|
2021-06-18 15:15:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to build eacl table: %w", err)
|
|
|
|
}
|
2021-06-28 13:20:11 +00:00
|
|
|
bearerTokens, err := buildBearerTokens(options.NeoFSKey, table, lifetime, options.GatesPublicKeys)
|
2021-06-18 15:15:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to build bearer tokens: %w", err)
|
|
|
|
}
|
|
|
|
for i, gateKey := range options.GatesPublicKeys {
|
|
|
|
gates[i] = accessbox.NewGateData(gateKey, bearerTokens[i])
|
|
|
|
}
|
|
|
|
|
2022-03-30 12:23:00 +00:00
|
|
|
if !options.SkipSessionRules {
|
2022-01-31 18:40:00 +00:00
|
|
|
sessionRules, err := buildContext(options.SessionTokenRules)
|
2021-06-16 14:07:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to build context for session token: %w", err)
|
|
|
|
}
|
2021-06-18 15:15:58 +00:00
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
var ownerID user.ID
|
|
|
|
user.IDFromKey(&ownerID, options.NeoFSKey.PrivateKey.PublicKey)
|
|
|
|
|
|
|
|
sessionTokens, err := buildSessionTokens(options.NeoFSKey, &ownerID, lifetime, sessionRules, options.GatesPublicKeys)
|
2021-06-18 15:15:58 +00:00
|
|
|
if err != nil {
|
2022-02-16 19:26:51 +00:00
|
|
|
return nil, fmt.Errorf("failed to biuild session token: %w", err)
|
2021-06-18 15:15:58 +00:00
|
|
|
}
|
2022-01-26 09:09:28 +00:00
|
|
|
for i, sessionTkns := range sessionTokens {
|
|
|
|
gates[i].SessionTokens = sessionTkns
|
2021-06-18 15:15:58 +00:00
|
|
|
}
|
2021-06-16 14:07:31 +00:00
|
|
|
}
|
2021-06-18 15:15:58 +00:00
|
|
|
|
|
|
|
return gates, nil
|
2021-06-16 14:07:31 +00:00
|
|
|
}
|