diff --git a/api/layer/container.go b/api/layer/container.go index 4956cf3b..faa4a604 100644 --- a/api/layer/container.go +++ b/api/layer/container.go @@ -141,7 +141,6 @@ func (n *Layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da SessionToken: p.SessionContainerCreation, CreationTime: bktInfo.Created, AdditionalAttributes: attributes, - BasicACL: 0, // means APE }) if err != nil { return nil, fmt.Errorf("create container: %w", err) diff --git a/api/layer/frostfs/frostfs.go b/api/layer/frostfs/frostfs.go index 26340dc8..f813d0b8 100644 --- a/api/layer/frostfs/frostfs.go +++ b/api/layer/frostfs/frostfs.go @@ -9,13 +9,13 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" - "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" ) // PrmContainerCreate groups parameters of FrostFS.CreateContainer operation. @@ -38,13 +38,19 @@ type PrmContainerCreate struct { // Token of the container's creation session. Nil means session absence. SessionToken *session.Container - // Basic ACL of the container. - BasicACL acl.Basic - // Attributes for optional parameters. AdditionalAttributes [][2]string } +// PrmAddContainerPolicyChain groups parameter of FrostFS.AddContainerPolicyChain operation. +type PrmAddContainerPolicyChain struct { + // ContainerID is a container identifier. + ContainerID cid.ID + + // Chain is Access Policy Engine chain that contains rules which provide access to specific actions in container. + Chain chain.Chain +} + // PrmContainer groups parameters of FrostFS.Container operation. type PrmContainer struct { // Container identifier. @@ -239,6 +245,10 @@ type FrostFS interface { // prevented the container from being created. CreateContainer(context.Context, PrmContainerCreate) (*ContainerCreateResult, error) + // AddContainerPolicyChain create new policy chain for container. + // Can be invoked only by container owner. + AddContainerPolicyChain(context.Context, PrmAddContainerPolicyChain) error + // Container reads a container from FrostFS by ID. // // It returns exactly one non-nil value. It returns any error encountered which diff --git a/api/layer/frostfs_mock.go b/api/layer/frostfs_mock.go index 1b29fca5..1c86d24b 100644 --- a/api/layer/frostfs_mock.go +++ b/api/layer/frostfs_mock.go @@ -5,6 +5,7 @@ import ( "context" "crypto/rand" "crypto/sha256" + "errors" "fmt" "io" "strings" @@ -25,6 +26,7 @@ import ( oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" ) @@ -61,13 +63,14 @@ func (k *FeatureSettingsMock) FormContainerZone(ns string) string { return ns + ".ns" } -type TestFrostFS struct { - frostfs.FrostFS +var _ frostfs.FrostFS = (*TestFrostFS)(nil) +type TestFrostFS struct { objects map[string]*object.Object objectErrors map[string]error objectPutErrors map[string]error containers map[string]*container.Container + chains map[string][]chain.Chain currentEpoch uint64 key *keys.PrivateKey } @@ -78,6 +81,7 @@ func NewTestFrostFS(key *keys.PrivateKey) *TestFrostFS { objectErrors: make(map[string]error), objectPutErrors: make(map[string]error), containers: make(map[string]*container.Container), + chains: make(map[string][]chain.Chain), key: key, } } @@ -145,7 +149,6 @@ func (t *TestFrostFS) CreateContainer(_ context.Context, prm frostfs.PrmContaine cnr.Init() cnr.SetOwner(prm.Creator) cnr.SetPlacementPolicy(prm.Policy) - cnr.SetBasicACL(prm.BasicACL) creationTime := prm.CreationTime if creationTime.IsZero() { @@ -174,6 +177,7 @@ func (t *TestFrostFS) CreateContainer(_ context.Context, prm frostfs.PrmContaine var id cid.ID id.SetSHA256(sha256.Sum256(b)) t.containers[id.EncodeToString()] = &cnr + t.chains[id.EncodeToString()] = []chain.Chain{} return &frostfs.ContainerCreateResult{ContainerID: id}, nil } @@ -455,6 +459,17 @@ func (t *TestFrostFS) PatchObject(ctx context.Context, prm frostfs.PrmObjectPatc return newID, nil } +func (t *TestFrostFS) AddContainerPolicyChain(_ context.Context, prm frostfs.PrmAddContainerPolicyChain) error { + list, ok := t.chains[prm.ContainerID.EncodeToString()] + if !ok { + return errors.New("container not found") + } + + t.chains[prm.ContainerID.EncodeToString()] = append(list, prm.Chain) + + return nil +} + func (t *TestFrostFS) checkAccess(cnrID cid.ID, owner user.ID) bool { cnr, ok := t.containers[cnrID.EncodeToString()] if !ok { diff --git a/authmate/authmate.go b/authmate/authmate.go index ef48d8c2..616ec986 100644 --- a/authmate/authmate.go +++ b/authmate/authmate.go @@ -33,7 +33,7 @@ import ( // PrmContainerCreate groups parameters of containers created by authmate. type PrmContainerCreate struct { // FrostFS identifier of the container creator. - Owner user.ID + Owner *keys.PublicKey // Container placement policy. Policy netmap.PlacementPolicy diff --git a/cmd/s3-authmate/modules/issue-secret.go b/cmd/s3-authmate/modules/issue-secret.go index b9df1ebe..7970baf0 100644 --- a/cmd/s3-authmate/modules/issue-secret.go +++ b/cmd/s3-authmate/modules/issue-secret.go @@ -14,7 +14,6 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet" 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/user" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -227,10 +226,9 @@ func createAccessBox(ctx context.Context, frostFS *frostfs.AuthmateFrostFS, key prm := authmate.PrmContainerCreate{ FriendlyName: friendlyName, + Owner: key.PublicKey(), } - user.IDFromKey(&prm.Owner, key.PrivateKey.PublicKey) - if err := prm.Policy.DecodeString(placementPolicy); err != nil { return cid.ID{}, fmt.Errorf("failed to build placement policy: %w", err) } diff --git a/internal/frostfs/authmate.go b/internal/frostfs/authmate.go index 97fe13d5..100939a1 100644 --- a/internal/frostfs/authmate.go +++ b/internal/frostfs/authmate.go @@ -3,6 +3,7 @@ package frostfs import ( "bytes" "context" + "encoding/hex" "fmt" "io" "strconv" @@ -16,10 +17,12 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/crdt" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs" - "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" + "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" "go.uber.org/zap" ) @@ -55,21 +58,61 @@ func (x *AuthmateFrostFS) TimeToEpoch(ctx context.Context, futureTime time.Time) // 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) + var owner user.ID + owner.SetScriptHash(prm.Owner.GetScriptHash()) res, err := x.frostFS.CreateContainer(ctx, frostfs.PrmContainerCreate{ - Creator: prm.Owner, - Policy: prm.Policy, - Name: prm.FriendlyName, - BasicACL: basicACL, + Creator: owner, + Policy: prm.Policy, + Name: prm.FriendlyName, }) if err != nil { return cid.ID{}, err } + + ch := chain.Chain{ + ID: chain.ID("authmate/" + owner.String()), + Rules: []chain.Rule{ + { + Status: chain.Allow, + Actions: chain.Actions{Names: []string{"*"}}, + Resources: chain.Resources{Names: []string{ + fmt.Sprintf(native.ResourceFormatRootContainer, res.ContainerID), + fmt.Sprintf(native.ResourceFormatRootContainerObjects, res.ContainerID), + }}, + Condition: []chain.Condition{{ + Op: chain.CondStringEquals, + Kind: chain.KindRequest, + Key: native.PropertyKeyActorPublicKey, + Value: hex.EncodeToString(prm.Owner.Bytes()), + }}, + }, + { + Status: chain.Allow, + Actions: chain.Actions{Names: []string{ + native.MethodGetContainer, + native.MethodGetObject, + native.MethodHeadObject, + native.MethodSearchObject, + native.MethodRangeObject, + native.MethodHashObject, + }}, + Resources: chain.Resources{Names: []string{ + fmt.Sprintf(native.ResourceFormatRootContainer, res.ContainerID), + fmt.Sprintf(native.ResourceFormatRootContainerObjects, res.ContainerID), + }}, + }, + }, + } + + err = x.frostFS.AddContainerPolicyChain(ctx, frostfs.PrmAddContainerPolicyChain{ + ContainerID: res.ContainerID, + Chain: ch, + }) + if err != nil { + return cid.ID{}, err + } + return res.ContainerID, nil } diff --git a/internal/frostfs/authmate_test.go b/internal/frostfs/authmate_test.go index 0c05c314..7b81c61e 100644 --- a/internal/frostfs/authmate_test.go +++ b/internal/frostfs/authmate_test.go @@ -47,7 +47,7 @@ func TestCredsObject(t *testing.T) { cnrID, err := frostfs.CreateContainer(ctx, authmate.PrmContainerCreate{ FriendlyName: bktName, - Owner: userID, + Owner: key.PublicKey(), }) require.NoError(t, err) diff --git a/internal/frostfs/frostfs.go b/internal/frostfs/frostfs.go index 03f8a7f6..24de01ac 100644 --- a/internal/frostfs/frostfs.go +++ b/internal/frostfs/frostfs.go @@ -12,6 +12,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/frostfs" frosterr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/errors" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/util" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" @@ -93,7 +94,6 @@ func (x *FrostFS) CreateContainer(ctx context.Context, prm frostfs.PrmContainerC cnr.Init() cnr.SetPlacementPolicy(prm.Policy) cnr.SetOwner(prm.Creator) - cnr.SetBasicACL(prm.BasicACL) creationTime := prm.CreationTime if creationTime.IsZero() { @@ -135,6 +135,25 @@ func (x *FrostFS) CreateContainer(ctx context.Context, prm frostfs.PrmContainerC }, handleObjectError("save container via connection pool", err) } +// AddContainerPolicyChain implements frostfs.FrostFS interface method. +func (x *FrostFS) AddContainerPolicyChain(ctx context.Context, prm frostfs.PrmAddContainerPolicyChain) error { + data, err := prm.Chain.MarshalBinary() + if err != nil { + return err + } + + prmAddAPEChain := pool.PrmAddAPEChain{ + Target: ape.ChainTarget{ + TargetType: ape.TargetTypeContainer, + Name: prm.ContainerID.EncodeToString(), + }, + Chain: ape.Chain{Raw: data}, + } + + err = x.pool.AddAPEChain(ctx, prmAddAPEChain) + return handleObjectError("add ape chain to container", err) +} + // UserContainers implements layer.FrostFS interface method. func (x *FrostFS) UserContainers(ctx context.Context, layerPrm frostfs.PrmUserContainers) ([]cid.ID, error) { prm := pool.PrmContainerList{