package auth import ( "context" "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" ) // ScopedAccessParams bundles everything needed to authorize an action against a // resource that follows the personal-vs-org ownership model. // // It is deliberately a plain struct (not an interface over a model) so it works // both for locally-stored objects and for objects whose ownership is learned // from a downstream service response. type ScopedAccessParams struct { // Service is the resource type, used only to build the access-denied error. Service mservice.Type // PolicyRef is the org policy consulted when the object is org-owned. PolicyRef bson.ObjectID // CallerRef is the subject taken from the auth token. Never from the request body. CallerRef bson.ObjectID // OrgRef is the organization the object belongs to. For org-owned objects // caller membership in it is checked before policy enforcement. Personal // objects are deliberately exempt from the membership check so the owner // keeps access after leaving the organization; membership for personal // objects is enforced once, at create time (see ScopedDBImp.Create). OrgRef bson.ObjectID // ObjectRef is the concrete object being accessed. Passing it enables // object-level checks in the Enforcer; use bson.NilObjectID for scope-only. ObjectRef bson.ObjectID // OwnerKind / OwnerRef describe the object's actual ownership, read FROM the // object — not derived from the request. OwnerKind model.OwnerKind OwnerRef bson.ObjectID // Action is the operation being authorized. Action model.Action } // enforceOwnershipAccess decides access based on ownership and policy only. // It does NOT check organization membership — the caller must verify membership // before invoking this. Use ScopedAuthorizer.Authorize for the full // membership-aware access check. // // - personal (OwnerKindAccount): allowed iff OwnerRef == CallerRef. // - org (OwnerKindOrganization): allowed iff the Enforcer grants Action within OrgRef. // - anything else: denied by default. func enforceOwnershipAccess(ctx context.Context, enf Enforcer, p ScopedAccessParams) error { switch p.OwnerKind { case model.OwnerKindAccount: if p.OwnerRef == p.CallerRef { return nil } return merrors.AccessDenied(p.Service, string(p.Action), p.ObjectRef) case model.OwnerKindOrganization: ok, err := enf.Enforce(ctx, p.PolicyRef, p.CallerRef, p.OrgRef, p.ObjectRef, p.Action) if err != nil { return err } if !ok { return merrors.AccessDenied(p.Service, string(p.Action), p.ObjectRef) } return nil default: return merrors.AccessDenied(p.Service, string(p.Action), p.ObjectRef) } }