forked from TrueCloudLab/frostfs-node
[#1701] tree: Form $Tree:ID
resource property for APE
* Make `verifyClient`, `checkAPE` receive `treeID` from request body; * Make `newAPERequest` set `$Tree:ID` property * Add unit-test to check if a rule for `$Tree:ID` works Close #1701 Change-Id: I834fed366e8adfd4b5c07bf50aac09af6239991b Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
This commit is contained in:
parent
fbc623f34e
commit
979d4bb2ae
5 changed files with 76 additions and 31 deletions
|
@ -22,7 +22,7 @@ import (
|
|||
)
|
||||
|
||||
func (s *Service) newAPERequest(ctx context.Context, namespace string,
|
||||
cid cid.ID, operation acl.Op, role acl.Role, publicKey *keys.PublicKey,
|
||||
cid cid.ID, treeID string, operation acl.Op, role acl.Role, publicKey *keys.PublicKey,
|
||||
) (aperequest.Request, error) {
|
||||
schemaMethod, err := converter.SchemaMethodFromACLOperation(operation)
|
||||
if err != nil {
|
||||
|
@ -53,15 +53,19 @@ func (s *Service) newAPERequest(ctx context.Context, namespace string,
|
|||
resourceName = fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainerObjects, namespace, cid.EncodeToString())
|
||||
}
|
||||
|
||||
resProps := map[string]string{
|
||||
nativeschema.ProperyKeyTreeID: treeID,
|
||||
}
|
||||
|
||||
return aperequest.NewRequest(
|
||||
schemaMethod,
|
||||
aperequest.NewResource(resourceName, make(map[string]string)),
|
||||
aperequest.NewResource(resourceName, resProps),
|
||||
reqProps,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (s *Service) checkAPE(ctx context.Context, bt *bearer.Token,
|
||||
container *core.Container, cid cid.ID, operation acl.Op, role acl.Role, publicKey *keys.PublicKey,
|
||||
container *core.Container, cid cid.ID, treeID string, operation acl.Op, role acl.Role, publicKey *keys.PublicKey,
|
||||
) error {
|
||||
namespace := ""
|
||||
cntNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(container.Value).Zone(), ".ns")
|
||||
|
@ -69,7 +73,7 @@ func (s *Service) checkAPE(ctx context.Context, bt *bearer.Token,
|
|||
namespace = cntNamespace
|
||||
}
|
||||
|
||||
request, err := s.newAPERequest(ctx, namespace, cid, operation, role, publicKey)
|
||||
request, err := s.newAPERequest(ctx, namespace, cid, treeID, operation, role, publicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create ape request: %w", err)
|
||||
}
|
||||
|
|
|
@ -107,6 +107,45 @@ func TestCheckAPE(t *testing.T) {
|
|||
cid := cid.ID{}
|
||||
_ = cid.DecodeString(containerID)
|
||||
|
||||
t.Run("treeID rule", func(t *testing.T) {
|
||||
los := inmemory.NewInmemoryLocalStorage()
|
||||
mcs := inmemory.NewInmemoryMorphRuleChainStorage()
|
||||
fid := newFrostfsIDProviderMock(t)
|
||||
s := Service{
|
||||
cfg: cfg{
|
||||
frostfsidSubjectProvider: fid,
|
||||
},
|
||||
apeChecker: checkercore.New(los, mcs, fid, &stMock{}),
|
||||
}
|
||||
|
||||
mcs.AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(containerID), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.QuotaLimitReached,
|
||||
Actions: chain.Actions{Names: []string{nativeschema.MethodGetObject}},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainerObjects, containerID)},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Op: chain.CondStringEquals,
|
||||
Kind: chain.KindResource,
|
||||
Key: nativeschema.ProperyKeyTreeID,
|
||||
Value: versionTreeID,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MatchType: chain.MatchTypeFirstMatch,
|
||||
})
|
||||
|
||||
err := s.checkAPE(context.Background(), nil, rootCnr, cid, versionTreeID, acl.OpObjectGet, acl.RoleOwner, senderPrivateKey.PublicKey())
|
||||
|
||||
var chErr *checkercore.ChainRouterError
|
||||
require.ErrorAs(t, err, &chErr)
|
||||
require.Equal(t, chain.QuotaLimitReached, chErr.Status())
|
||||
})
|
||||
|
||||
t.Run("put non-tombstone rule won't affect tree remove", func(t *testing.T) {
|
||||
los := inmemory.NewInmemoryLocalStorage()
|
||||
mcs := inmemory.NewInmemoryMorphRuleChainStorage()
|
||||
|
@ -152,7 +191,7 @@ func TestCheckAPE(t *testing.T) {
|
|||
MatchType: chain.MatchTypeFirstMatch,
|
||||
})
|
||||
|
||||
err := s.checkAPE(context.Background(), nil, rootCnr, cid, acl.OpObjectDelete, acl.RoleOwner, senderPrivateKey.PublicKey())
|
||||
err := s.checkAPE(context.Background(), nil, rootCnr, cid, versionTreeID, acl.OpObjectDelete, acl.RoleOwner, senderPrivateKey.PublicKey())
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
|
@ -201,7 +240,7 @@ func TestCheckAPE(t *testing.T) {
|
|||
MatchType: chain.MatchTypeFirstMatch,
|
||||
})
|
||||
|
||||
err := s.checkAPE(context.Background(), nil, rootCnr, cid, acl.OpObjectPut, acl.RoleOwner, senderPrivateKey.PublicKey())
|
||||
err := s.checkAPE(context.Background(), nil, rootCnr, cid, versionTreeID, acl.OpObjectPut, acl.RoleOwner, senderPrivateKey.PublicKey())
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ func (s *Service) Add(ctx context.Context, req *AddRequest) (*AddResponse, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectPut)
|
||||
err := s.verifyClient(ctx, req, cid, req.GetBody().GetTreeId(), b.GetBearerToken(), acl.OpObjectPut)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func (s *Service) AddByPath(ctx context.Context, req *AddByPathRequest) (*AddByP
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectPut)
|
||||
err := s.verifyClient(ctx, req, cid, req.GetBody().GetTreeId(), b.GetBearerToken(), acl.OpObjectPut)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ func (s *Service) Remove(ctx context.Context, req *RemoveRequest) (*RemoveRespon
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectDelete)
|
||||
err := s.verifyClient(ctx, req, cid, req.GetBody().GetTreeId(), b.GetBearerToken(), acl.OpObjectDelete)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ func (s *Service) Move(ctx context.Context, req *MoveRequest) (*MoveResponse, er
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectPut)
|
||||
err := s.verifyClient(ctx, req, cid, req.GetBody().GetTreeId(), b.GetBearerToken(), acl.OpObjectPut)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -306,7 +306,7 @@ func (s *Service) GetNodeByPath(ctx context.Context, req *GetNodeByPathRequest)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectGet)
|
||||
err := s.verifyClient(ctx, req, cid, req.GetBody().GetTreeId(), b.GetBearerToken(), acl.OpObjectGet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ func (s *Service) GetSubTree(req *GetSubTreeRequest, srv TreeService_GetSubTreeS
|
|||
return err
|
||||
}
|
||||
|
||||
err := s.verifyClient(srv.Context(), req, cid, b.GetBearerToken(), acl.OpObjectGet)
|
||||
err := s.verifyClient(srv.Context(), req, cid, req.GetBody().GetTreeId(), b.GetBearerToken(), acl.OpObjectGet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ var (
|
|||
// Operation must be one of:
|
||||
// - 1. ObjectPut;
|
||||
// - 2. ObjectGet.
|
||||
func (s *Service) verifyClient(ctx context.Context, req message, cid cidSDK.ID, rawBearer []byte, op acl.Op) error {
|
||||
func (s *Service) verifyClient(ctx context.Context, req message, cid cidSDK.ID, treeID string, rawBearer []byte, op acl.Op) error {
|
||||
err := verifyMessage(req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -64,7 +64,7 @@ func (s *Service) verifyClient(ctx context.Context, req message, cid cidSDK.ID,
|
|||
return fmt.Errorf("can't get request role: %w", err)
|
||||
}
|
||||
|
||||
if err = s.checkAPE(ctx, bt, cnr, cid, op, role, pubKey); err != nil {
|
||||
if err = s.checkAPE(ctx, bt, cnr, cid, treeID, op, role, pubKey); err != nil {
|
||||
return apeErr(err)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -31,6 +31,8 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const versionTreeID = "version"
|
||||
|
||||
type dummyNetmapSource struct {
|
||||
netmap.Source
|
||||
}
|
||||
|
@ -168,26 +170,26 @@ func TestMessageSign(t *testing.T) {
|
|||
cnr.Value.SetBasicACL(acl.PublicRW)
|
||||
|
||||
t.Run("missing signature, no panic", func(t *testing.T) {
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid2, versionTreeID, nil, op))
|
||||
})
|
||||
|
||||
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, nil, op))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, nil, op))
|
||||
|
||||
t.Run("invalid CID", func(t *testing.T) {
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid2, versionTreeID, nil, op))
|
||||
})
|
||||
|
||||
cnr.Value.SetBasicACL(acl.Private)
|
||||
|
||||
t.Run("extension disabled", func(t *testing.T) {
|
||||
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid2, versionTreeID, nil, op))
|
||||
})
|
||||
|
||||
t.Run("invalid key", func(t *testing.T) {
|
||||
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, nil, op))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, nil, op))
|
||||
})
|
||||
|
||||
t.Run("bearer", func(t *testing.T) {
|
||||
|
@ -200,7 +202,7 @@ func TestMessageSign(t *testing.T) {
|
|||
t.Run("invalid bearer", func(t *testing.T) {
|
||||
req.Body.BearerToken = []byte{0xFF}
|
||||
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
})
|
||||
|
||||
t.Run("invalid bearer CID", func(t *testing.T) {
|
||||
|
@ -209,7 +211,7 @@ func TestMessageSign(t *testing.T) {
|
|||
req.Body.BearerToken = bt.Marshal()
|
||||
|
||||
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
})
|
||||
t.Run("invalid bearer owner", func(t *testing.T) {
|
||||
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
|
||||
|
@ -217,7 +219,7 @@ func TestMessageSign(t *testing.T) {
|
|||
req.Body.BearerToken = bt.Marshal()
|
||||
|
||||
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
})
|
||||
t.Run("invalid bearer signature", func(t *testing.T) {
|
||||
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
|
||||
|
@ -229,7 +231,7 @@ func TestMessageSign(t *testing.T) {
|
|||
req.Body.BearerToken = bv2.StableMarshal(nil)
|
||||
|
||||
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
})
|
||||
|
||||
t.Run("impersonate", func(t *testing.T) {
|
||||
|
@ -241,8 +243,8 @@ func TestMessageSign(t *testing.T) {
|
|||
req.Body.BearerToken = bt.Marshal()
|
||||
|
||||
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectGet))
|
||||
})
|
||||
|
||||
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
|
||||
|
@ -252,18 +254,18 @@ func TestMessageSign(t *testing.T) {
|
|||
|
||||
t.Run("put and get", func(t *testing.T) {
|
||||
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectGet))
|
||||
})
|
||||
t.Run("only get", func(t *testing.T) {
|
||||
require.NoError(t, SignMessage(req, &privs[2].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.NoError(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectGet))
|
||||
})
|
||||
t.Run("none", func(t *testing.T) {
|
||||
require.NoError(t, SignMessage(req, &privs[3].PrivateKey))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
||||
require.Error(t, s.verifyClient(context.Background(), req, cid1, versionTreeID, req.GetBody().GetBearerToken(), acl.OpObjectGet))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue