Files
sxchnge/backend/pkg/auth/scoped.go
T

72 lines
2.7 KiB
Go

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)
}
}