Bootstrap copied BFF and local accounts with public Sendico contracts (#34)
This commit is contained in:
1 parent
67dc5b5b1e
commit
7062672a87
556 files changed
+42291
-777
No files matched your search
@@ -0,0 +1,256 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/merrors"
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/model"
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/mservice"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// testOwned is a minimal model.OwnerBound used exclusively by ScopedDB tests.
|
||||
type testOwned struct {
|
||||
id bson.ObjectID
|
||||
orgRef bson.ObjectID
|
||||
kind model.OwnerKind
|
||||
ownerRef bson.ObjectID
|
||||
}
|
||||
|
||||
func (o *testOwned) GetID() *bson.ObjectID { return &o.id }
|
||||
func (o *testOwned) SetID(id bson.ObjectID) { o.id = id }
|
||||
func (o *testOwned) Update() {}
|
||||
func (o *testOwned) Collection() string { return "test_owned" }
|
||||
func (o *testOwned) GetOrganizationRef() bson.ObjectID { return o.orgRef }
|
||||
func (o *testOwned) SetOrganizationRef(ref bson.ObjectID) { o.orgRef = ref }
|
||||
func (o *testOwned) GetOwnerKind() model.OwnerKind { return o.kind }
|
||||
func (o *testOwned) GetOwnerRef() bson.ObjectID { return o.ownerRef }
|
||||
|
||||
var _ model.OwnerBound = (*testOwned)(nil)
|
||||
|
||||
// fakeOwnerLoader returns the pre-built object or the configured error.
|
||||
type fakeOwnerLoader struct {
|
||||
obj *testOwned
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeOwnerLoader) Get(_ context.Context, _ bson.ObjectID, result *testOwned) error {
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
*result = *f.obj
|
||||
return nil
|
||||
}
|
||||
|
||||
// fakeMembership controls whether IsMember returns true/false/error.
|
||||
type fakeMembership struct {
|
||||
member bool
|
||||
err error
|
||||
called bool
|
||||
}
|
||||
|
||||
func (f *fakeMembership) IsMember(_ context.Context, _, _ bson.ObjectID) (bool, error) {
|
||||
f.called = true
|
||||
return f.member, f.err
|
||||
}
|
||||
|
||||
func newTestScopedDB(loader *fakeOwnerLoader, membership *fakeMembership, enf Enforcer) *ScopedDB[*testOwned] {
|
||||
authorizer := &ScopedAuthorizer{
|
||||
Logger: zap.NewNop(),
|
||||
Membership: membership,
|
||||
Enforcer: enf,
|
||||
}
|
||||
return &ScopedDB[*testOwned]{
|
||||
DB: loader,
|
||||
Logger: zap.NewNop(),
|
||||
Authorizer: authorizer,
|
||||
OrgPolicyRef: bson.NewObjectID(),
|
||||
Collection: mservice.Type("test_owned"),
|
||||
New: func() *testOwned { return &testOwned{} },
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopedDB_PersonalOwner_Allowed(t *testing.T) {
|
||||
caller := bson.NewObjectID()
|
||||
objectRef := bson.NewObjectID()
|
||||
obj := &testOwned{
|
||||
id: objectRef,
|
||||
orgRef: bson.NewObjectID(),
|
||||
kind: model.OwnerKindAccount,
|
||||
ownerRef: caller, // owner == caller
|
||||
}
|
||||
enf := &fakeEnforcer{}
|
||||
db := newTestScopedDB(&fakeOwnerLoader{obj: obj}, &fakeMembership{member: true}, enf)
|
||||
|
||||
result, err := db.LoadAndEnforce(context.Background(), caller, objectRef, model.ActionRead)
|
||||
if err != nil {
|
||||
t.Fatalf("expected allow, got %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("expected non-nil result on success")
|
||||
}
|
||||
if enf.called {
|
||||
t.Fatal("personal access must not consult the enforcer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopedDB_PersonalOwner_Denied(t *testing.T) {
|
||||
caller := bson.NewObjectID()
|
||||
other := bson.NewObjectID()
|
||||
objectRef := bson.NewObjectID()
|
||||
obj := &testOwned{
|
||||
id: objectRef,
|
||||
orgRef: bson.NewObjectID(),
|
||||
kind: model.OwnerKindAccount,
|
||||
ownerRef: other, // owner != caller
|
||||
}
|
||||
enf := &fakeEnforcer{}
|
||||
db := newTestScopedDB(&fakeOwnerLoader{obj: obj}, &fakeMembership{member: true}, enf)
|
||||
|
||||
_, err := db.LoadAndEnforce(context.Background(), caller, objectRef, model.ActionRead)
|
||||
if !errors.Is(err, merrors.ErrAccessDenied) {
|
||||
t.Fatalf("expected access denied, got %v", err)
|
||||
}
|
||||
if enf.called {
|
||||
t.Fatal("personal access must not consult the enforcer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopedDB_OrgOwner_Allowed(t *testing.T) {
|
||||
caller := bson.NewObjectID()
|
||||
orgRef := bson.NewObjectID()
|
||||
objectRef := bson.NewObjectID()
|
||||
obj := &testOwned{
|
||||
id: objectRef,
|
||||
orgRef: orgRef,
|
||||
kind: model.OwnerKindOrganization,
|
||||
ownerRef: orgRef,
|
||||
}
|
||||
enf := &fakeEnforcer{allow: true}
|
||||
db := newTestScopedDB(&fakeOwnerLoader{obj: obj}, &fakeMembership{member: true}, enf)
|
||||
|
||||
result, err := db.LoadAndEnforce(context.Background(), caller, objectRef, model.ActionRead)
|
||||
if err != nil {
|
||||
t.Fatalf("expected allow, got %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("expected non-nil result on success")
|
||||
}
|
||||
if !enf.called {
|
||||
t.Fatal("org access must consult the enforcer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopedDB_OrgOwner_Denied(t *testing.T) {
|
||||
caller := bson.NewObjectID()
|
||||
orgRef := bson.NewObjectID()
|
||||
objectRef := bson.NewObjectID()
|
||||
obj := &testOwned{
|
||||
id: objectRef,
|
||||
orgRef: orgRef,
|
||||
kind: model.OwnerKindOrganization,
|
||||
ownerRef: orgRef,
|
||||
}
|
||||
enf := &fakeEnforcer{allow: false}
|
||||
db := newTestScopedDB(&fakeOwnerLoader{obj: obj}, &fakeMembership{member: true}, enf)
|
||||
|
||||
_, err := db.LoadAndEnforce(context.Background(), caller, objectRef, model.ActionRead)
|
||||
if !errors.Is(err, merrors.ErrAccessDenied) {
|
||||
t.Fatalf("expected access denied, got %v", err)
|
||||
}
|
||||
if !enf.called {
|
||||
t.Fatal("org access must consult the enforcer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopedDB_OrgOwner_EnforcerError_Propagated(t *testing.T) {
|
||||
boom := merrors.Define("enforcer backend down")
|
||||
caller := bson.NewObjectID()
|
||||
orgRef := bson.NewObjectID()
|
||||
objectRef := bson.NewObjectID()
|
||||
obj := &testOwned{
|
||||
id: objectRef,
|
||||
orgRef: orgRef,
|
||||
kind: model.OwnerKindOrganization,
|
||||
ownerRef: orgRef,
|
||||
}
|
||||
enf := &fakeEnforcer{err: boom}
|
||||
db := newTestScopedDB(&fakeOwnerLoader{obj: obj}, &fakeMembership{member: true}, enf)
|
||||
|
||||
_, err := db.LoadAndEnforce(context.Background(), caller, objectRef, model.ActionRead)
|
||||
if !errors.Is(err, boom) {
|
||||
t.Fatalf("expected underlying error propagated, got %v", err)
|
||||
}
|
||||
if errors.Is(err, merrors.ErrAccessDenied) {
|
||||
t.Fatal("enforcer internal error must not be masked as access denied")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopedDB_DBError_Propagated(t *testing.T) {
|
||||
dbErr := merrors.Define("mongo unavailable")
|
||||
db := newTestScopedDB(&fakeOwnerLoader{err: dbErr}, &fakeMembership{member: true}, &fakeEnforcer{})
|
||||
|
||||
_, err := db.LoadAndEnforce(context.Background(), bson.NewObjectID(), bson.NewObjectID(), model.ActionRead)
|
||||
if !errors.Is(err, dbErr) {
|
||||
t.Fatalf("expected DB error propagated, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestScopedDB_PersonalOwner_NonMember_Denied is the main regression case:
|
||||
// even when ownerRef == callerRef, a non-member of the organization must be
|
||||
// denied before ownership is checked, so the tenant boundary is enforced.
|
||||
// TestScopedDB_PersonalOwner_NonMember_Allowed: personal resource, caller is the
|
||||
// owner but is NOT a member of the org. Membership is not consulted for
|
||||
// account-owned resources — ownership alone is sufficient.
|
||||
func TestScopedDB_PersonalOwner_NonMember_Allowed(t *testing.T) {
|
||||
caller := bson.NewObjectID()
|
||||
objectRef := bson.NewObjectID()
|
||||
obj := &testOwned{
|
||||
id: objectRef,
|
||||
orgRef: bson.NewObjectID(),
|
||||
kind: model.OwnerKindAccount,
|
||||
ownerRef: caller, // owner == caller, membership is irrelevant
|
||||
}
|
||||
membership := &fakeMembership{member: false}
|
||||
enf := &fakeEnforcer{}
|
||||
db := newTestScopedDB(&fakeOwnerLoader{obj: obj}, membership, enf)
|
||||
|
||||
result, err := db.LoadAndEnforce(context.Background(), caller, objectRef, model.ActionRead)
|
||||
if err != nil {
|
||||
t.Fatalf("expected allow for personal resource owner regardless of membership, got %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("expected non-nil result on success")
|
||||
}
|
||||
if enf.called {
|
||||
t.Fatal("enforcer must not be called for account-owned resource")
|
||||
}
|
||||
}
|
||||
|
||||
// TestScopedDB_OrgOwner_NonMember_Denied: org-owned object, caller is not a
|
||||
// member — must be denied before the policy enforcer is consulted.
|
||||
func TestScopedDB_OrgOwner_NonMember_Denied(t *testing.T) {
|
||||
caller := bson.NewObjectID()
|
||||
orgRef := bson.NewObjectID()
|
||||
objectRef := bson.NewObjectID()
|
||||
obj := &testOwned{
|
||||
id: objectRef,
|
||||
orgRef: orgRef,
|
||||
kind: model.OwnerKindOrganization,
|
||||
ownerRef: orgRef,
|
||||
}
|
||||
membership := &fakeMembership{member: false}
|
||||
enf := &fakeEnforcer{allow: true} // would allow if reached
|
||||
db := newTestScopedDB(&fakeOwnerLoader{obj: obj}, membership, enf)
|
||||
|
||||
_, err := db.LoadAndEnforce(context.Background(), caller, objectRef, model.ActionRead)
|
||||
if !errors.Is(err, merrors.ErrAccessDenied) {
|
||||
t.Fatalf("expected access denied for non-member org-owned object, got %v", err)
|
||||
}
|
||||
if enf.called {
|
||||
t.Fatal("enforcer must not be called when membership check fails")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user