forked from TrueCloudLab/frostfs-s3-gw
157 lines
4.7 KiB
Go
157 lines
4.7 KiB
Go
package frostfs
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"time"
|
|
|
|
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/crdt"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
|
)
|
|
|
|
const (
|
|
accessBoxCRDTNameAttr = "S3-Access-Box-CRDT-Name"
|
|
)
|
|
|
|
// AuthmateFrostFS is a mediator which implements authmate.FrostFS through pool.Pool.
|
|
type AuthmateFrostFS struct {
|
|
frostFS *FrostFS
|
|
}
|
|
|
|
// NewAuthmateFrostFS creates new AuthmateFrostFS using provided pool.Pool.
|
|
func NewAuthmateFrostFS(p *pool.Pool) *AuthmateFrostFS {
|
|
return &AuthmateFrostFS{frostFS: NewFrostFS(p)}
|
|
}
|
|
|
|
// ContainerExists implements authmate.FrostFS interface method.
|
|
func (x *AuthmateFrostFS) ContainerExists(ctx context.Context, idCnr cid.ID) error {
|
|
_, err := x.frostFS.Container(ctx, idCnr)
|
|
if err != nil {
|
|
return fmt.Errorf("get container via connection pool: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TimeToEpoch implements authmate.FrostFS interface method.
|
|
func (x *AuthmateFrostFS) TimeToEpoch(ctx context.Context, futureTime time.Time) (uint64, uint64, error) {
|
|
return x.frostFS.TimeToEpoch(ctx, time.Now(), futureTime)
|
|
}
|
|
|
|
// CreateContainer implements authmate.FrostFS interface method.
|
|
func (x *AuthmateFrostFS) CreateContainer(ctx context.Context, prm authmate.PrmContainerCreate) (cid.ID, error) {
|
|
basicACL := acl.Private
|
|
// allow reading objects to OTHERS in order to provide read access to S3 gateways
|
|
basicACL.AllowOp(acl.OpObjectGet, acl.RoleOthers)
|
|
basicACL.AllowOp(acl.OpObjectHead, acl.RoleOthers)
|
|
basicACL.AllowOp(acl.OpObjectSearch, acl.RoleOthers)
|
|
|
|
return x.frostFS.CreateContainer(ctx, layer.PrmContainerCreate{
|
|
Creator: prm.Owner,
|
|
Policy: prm.Policy,
|
|
Name: prm.FriendlyName,
|
|
BasicACL: basicACL,
|
|
})
|
|
}
|
|
|
|
// GetCredsPayload implements authmate.FrostFS interface method.
|
|
func (x *AuthmateFrostFS) GetCredsPayload(ctx context.Context, addr oid.Address) ([]byte, error) {
|
|
versions, err := x.getCredVersions(ctx, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
credObjID := addr.Object()
|
|
if last := versions.GetLast(); last != nil {
|
|
credObjID = last.OjbID
|
|
}
|
|
|
|
res, err := x.frostFS.ReadObject(ctx, layer.PrmObjectRead{
|
|
Container: addr.Container(),
|
|
Object: credObjID,
|
|
WithPayload: true,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer res.Payload.Close()
|
|
|
|
return io.ReadAll(res.Payload)
|
|
}
|
|
|
|
// CreateObject implements authmate.FrostFS interface method.
|
|
func (x *AuthmateFrostFS) CreateObject(ctx context.Context, prm tokens.PrmObjectCreate) (oid.ID, error) {
|
|
attributes := [][2]string{{objectv2.SysAttributeExpEpoch, strconv.FormatUint(prm.ExpirationEpoch, 10)}}
|
|
|
|
if prm.NewVersionFor != nil {
|
|
var addr oid.Address
|
|
addr.SetContainer(prm.Container)
|
|
addr.SetObject(*prm.NewVersionFor)
|
|
|
|
versions, err := x.getCredVersions(ctx, addr)
|
|
if err != nil {
|
|
return oid.ID{}, err
|
|
}
|
|
|
|
if versions.GetLast() == nil {
|
|
versions.AppendVersion(&crdt.ObjectVersion{OjbID: addr.Object()})
|
|
}
|
|
|
|
for key, val := range versions.GetCRDTHeaders() {
|
|
attributes = append(attributes, [2]string{key, val})
|
|
}
|
|
|
|
attributes = append(attributes, [2]string{accessBoxCRDTNameAttr, versions.Name()})
|
|
}
|
|
|
|
return x.frostFS.CreateObject(ctx, layer.PrmObjectCreate{
|
|
Creator: prm.Creator,
|
|
Container: prm.Container,
|
|
Filepath: prm.Filepath,
|
|
Attributes: attributes,
|
|
Payload: bytes.NewReader(prm.Payload),
|
|
})
|
|
}
|
|
|
|
func (x *AuthmateFrostFS) getCredVersions(ctx context.Context, addr oid.Address) (*crdt.ObjectVersions, error) {
|
|
objCredSystemName := credVersionSysName(addr.Container(), addr.Object())
|
|
credVersions, err := x.frostFS.SearchObjects(ctx, layer.PrmObjectSearch{
|
|
Container: addr.Container(),
|
|
ExactAttribute: [2]string{accessBoxCRDTNameAttr, objCredSystemName},
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("search s3 access boxes: %w", err)
|
|
}
|
|
|
|
versions := crdt.NewObjectVersions(objCredSystemName)
|
|
|
|
for _, id := range credVersions {
|
|
objVersion, err := x.frostFS.ReadObject(ctx, layer.PrmObjectRead{
|
|
Container: addr.Container(),
|
|
Object: id,
|
|
WithHeader: true,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("head crdt access box '%s': %w", id.EncodeToString(), err)
|
|
}
|
|
|
|
versions.AppendVersion(crdt.NewObjectVersion(objVersion.Head))
|
|
}
|
|
|
|
return versions, nil
|
|
}
|
|
|
|
func credVersionSysName(cnrID cid.ID, objID oid.ID) string {
|
|
return cnrID.EncodeToString() + "0" + objID.EncodeToString()
|
|
}
|