Bootstrap copied BFF and local accounts with public Sendico contracts (#34)

This commit is contained in:
Stephan D committed 2026-09-12 13:37:48 +02:00
1 parent 67dc5b5b1e
commit 7062672a87
556 files changed
+42291 -777

No files matched your search

+256
View File
@@ -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")
}
}