fx build fix
Some checks failed
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/fx/1 Pipeline failed
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/fx/2 Pipeline failed

This commit is contained in:
Stephan D
2025-11-08 00:40:01 +01:00
parent 49b86efecb
commit d367dddbbd
98 changed files with 3983 additions and 5063 deletions

View File

@@ -1,43 +0,0 @@
package auth
import (
"context"
"github.com/tech/sendico/pkg/db/template"
"github.com/tech/sendico/pkg/model"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// TaggableDB implements tag operations with permission checking
type TaggableDB[T model.PermissionBoundStorable] interface {
// AddTag adds a tag to an entity with permission checking
AddTag(ctx context.Context, accountRef, objectRef, tagRef primitive.ObjectID) error
// RemoveTagd removes a tags from the collection using organizationRef with permission checking
RemoveTags(ctx context.Context, accountRef, organizationRef, tagRef primitive.ObjectID) error
// RemoveTag removes a tag from an entity with permission checking
RemoveTag(ctx context.Context, accountRef, objectRef, tagRef primitive.ObjectID) error
// AddTags adds multiple tags to an entity with permission checking
AddTags(ctx context.Context, accountRef, objectRef primitive.ObjectID, tagRefs []primitive.ObjectID) error
// SetTags sets the tags for an entity with permission checking
SetTags(ctx context.Context, accountRef, objectRef primitive.ObjectID, tagRefs []primitive.ObjectID) error
// RemoveAllTags removes all tags from an entity with permission checking
RemoveAllTags(ctx context.Context, accountRef, objectRef primitive.ObjectID) error
// GetTags gets the tags for an entity with permission checking
GetTags(ctx context.Context, accountRef, objectRef primitive.ObjectID) ([]primitive.ObjectID, error)
// HasTag checks if an entity has a specific tag with permission checking
HasTag(ctx context.Context, accountRef, objectRef, tagRef primitive.ObjectID) (bool, error)
// FindByTag finds all entities that have a specific tag with permission checking
FindByTag(ctx context.Context, accountRef, tagRef primitive.ObjectID) ([]T, error)
// FindByTags finds all entities that have any of the specified tags with permission checking
FindByTags(ctx context.Context, accountRef primitive.ObjectID, tagRefs []primitive.ObjectID) ([]T, error)
}
// NewTaggableDBImp creates a new auth.TaggableDB instance
func NewTaggableDB[T model.PermissionBoundStorable](
dbImp *template.DBImp[T],
enforcer Enforcer,
createEmpty func() T,
getTaggable func(T) *model.Taggable,
) TaggableDB[T] {
return newTaggableDBImp(dbImp, enforcer, createEmpty, getTaggable)
}

View File

@@ -1,302 +0,0 @@
package auth
import (
"context"
"github.com/tech/sendico/pkg/db/repository"
"github.com/tech/sendico/pkg/db/repository/builder"
"github.com/tech/sendico/pkg/db/template"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mutil/mzap"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.uber.org/zap"
)
// taggableDBImp implements tag operations with permission checking
type taggableDBImp[T model.PermissionBoundStorable] struct {
dbImp *template.DBImp[T]
logger mlogger.Logger
enforcer Enforcer
createEmpty func() T
getTaggable func(T) *model.Taggable
}
func newTaggableDBImp[T model.PermissionBoundStorable](
dbImp *template.DBImp[T],
enforcer Enforcer,
createEmpty func() T,
getTaggable func(T) *model.Taggable,
) TaggableDB[T] {
return &taggableDBImp[T]{
dbImp: dbImp,
logger: dbImp.Logger.Named("taggable"),
enforcer: enforcer,
createEmpty: createEmpty,
getTaggable: getTaggable,
}
}
func (db *taggableDBImp[T]) AddTag(ctx context.Context, accountRef, objectRef, tagRef primitive.ObjectID) error {
// Check permissions using enforceObject helper
if err := enforceObjectByRef(ctx, db.dbImp, db.enforcer, model.ActionUpdate, accountRef, objectRef); err != nil {
return err
}
// Add the tag
patch := repository.Patch().AddToSet(repository.TagRefsField(), tagRef)
if err := db.dbImp.Patch(ctx, objectRef, patch); err != nil {
db.logger.Warn("Failed to add tag to object", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("object_ref", objectRef), mzap.ObjRef("tag_ref", tagRef))
return err
}
db.logger.Debug("Successfully added tag to object", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("object_ref", objectRef), mzap.ObjRef("tag_ref", tagRef))
return nil
}
func (db *taggableDBImp[T]) removeTag(ctx context.Context, accountRef, targetRef, tagRef primitive.ObjectID, query builder.Query) error {
// Check permissions using enforceObject helper
if err := enforceObject(ctx, db.dbImp, db.enforcer, model.ActionUpdate, accountRef, query); err != nil {
db.logger.Debug("Error enforcing permissions for removing tag", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("target_ref", targetRef), mzap.ObjRef("tag_ref", tagRef))
return err
}
// Remove the tag
patch := repository.Patch().Pull(repository.TagRefsField(), tagRef)
patched, err := db.dbImp.PatchMany(ctx, query, patch)
if err != nil {
db.logger.Warn("Failed to remove tag from object", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("target_ref", targetRef), mzap.ObjRef("tag_ref", tagRef))
return err
}
db.logger.Debug("Successfully removed tag from object", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("target_ref", targetRef), mzap.ObjRef("tag_ref", tagRef), zap.Int("patched_count", patched))
return nil
}
func (db *taggableDBImp[T]) RemoveTags(ctx context.Context, accountRef, organizationRef, tagRef primitive.ObjectID) error {
return db.removeTag(ctx, accountRef, primitive.NilObjectID, tagRef, repository.OrgFilter(organizationRef))
}
func (db *taggableDBImp[T]) RemoveTag(ctx context.Context, accountRef, objectRef, tagRef primitive.ObjectID) error {
return db.removeTag(ctx, accountRef, objectRef, tagRef, repository.IDFilter(objectRef))
}
// AddTags adds multiple tags to an entity with permission checking
func (db *taggableDBImp[T]) AddTags(ctx context.Context, accountRef, objectRef primitive.ObjectID, tagRefs []primitive.ObjectID) error {
// Check permissions using enforceObject helper
if err := enforceObjectByRef(ctx, db.dbImp, db.enforcer, model.ActionUpdate, accountRef, objectRef); err != nil {
return err
}
// Add the tags one by one using $addToSet to avoid duplicates
for _, tagRef := range tagRefs {
patch := repository.Patch().AddToSet(repository.TagRefsField(), tagRef)
if err := db.dbImp.Patch(ctx, objectRef, patch); err != nil {
db.logger.Warn("Failed to add tag to object", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("object_ref", objectRef), mzap.ObjRef("tag_ref", tagRef))
return err
}
}
db.logger.Debug("Successfully added tags to object", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("object_ref", objectRef), zap.Int("tag_count", len(tagRefs)))
return nil
}
// SetTags sets the tags for an entity with permission checking
func (db *taggableDBImp[T]) SetTags(ctx context.Context, accountRef, objectRef primitive.ObjectID, tagRefs []primitive.ObjectID) error {
// Check permissions using enforceObject helper
if err := enforceObjectByRef(ctx, db.dbImp, db.enforcer, model.ActionUpdate, accountRef, objectRef); err != nil {
return err
}
// Set the tags
patch := repository.Patch().Set(repository.TagRefsField(), tagRefs)
if err := db.dbImp.Patch(ctx, objectRef, patch); err != nil {
db.logger.Warn("Failed to set tags for object", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("object_ref", objectRef))
return err
}
db.logger.Debug("Successfully set tags for object", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("object_ref", objectRef), zap.Int("tag_count", len(tagRefs)))
return nil
}
// RemoveAllTags removes all tags from an entity with permission checking
func (db *taggableDBImp[T]) RemoveAllTags(ctx context.Context, accountRef, objectRef primitive.ObjectID) error {
// Check permissions using enforceObject helper
if err := enforceObjectByRef(ctx, db.dbImp, db.enforcer, model.ActionUpdate, accountRef, objectRef); err != nil {
return err
}
// Remove all tags by setting to empty array
patch := repository.Patch().Set(repository.TagRefsField(), []primitive.ObjectID{})
if err := db.dbImp.Patch(ctx, objectRef, patch); err != nil {
db.logger.Warn("Failed to remove all tags from object", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("object_ref", objectRef))
return err
}
db.logger.Debug("Successfully removed all tags from object", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("object_ref", objectRef))
return nil
}
// GetTags gets the tags for an entity with permission checking
func (db *taggableDBImp[T]) GetTags(ctx context.Context, accountRef, objectRef primitive.ObjectID) ([]primitive.ObjectID, error) {
// Check permissions using enforceObject helper
if err := enforceObjectByRef(ctx, db.dbImp, db.enforcer, model.ActionRead, accountRef, objectRef); err != nil {
return nil, err
}
// Get the object and extract tags
obj := db.createEmpty()
if err := db.dbImp.Get(ctx, objectRef, obj); err != nil {
db.logger.Warn("Failed to get object for retrieving tags", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("object_ref", objectRef))
return nil, err
}
// Get the tags
taggable := db.getTaggable(obj)
db.logger.Debug("Successfully retrieved tags for object", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("object_ref", objectRef), zap.Int("tag_count", len(taggable.TagRefs)))
return taggable.TagRefs, nil
}
// HasTag checks if an entity has a specific tag with permission checking
func (db *taggableDBImp[T]) HasTag(ctx context.Context, accountRef, objectRef, tagRef primitive.ObjectID) (bool, error) {
// Check permissions using enforceObject helper
if err := enforceObjectByRef(ctx, db.dbImp, db.enforcer, model.ActionRead, accountRef, objectRef); err != nil {
return false, err
}
// Get the object and check if the tag exists
obj := db.createEmpty()
if err := db.dbImp.Get(ctx, objectRef, obj); err != nil {
db.logger.Warn("Failed to get object for checking tag", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("object_ref", objectRef), mzap.ObjRef("tag_ref", tagRef))
return false, err
}
// Check if the tag exists
taggable := db.getTaggable(obj)
for _, existingTag := range taggable.TagRefs {
if existingTag == tagRef {
db.logger.Debug("Object has tag", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("object_ref", objectRef), mzap.ObjRef("tag_ref", tagRef))
return true, nil
}
}
db.logger.Debug("Object does not have tag", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("object_ref", objectRef), mzap.ObjRef("tag_ref", tagRef))
return false, nil
}
// FindByTag finds all entities that have a specific tag with permission checking
func (db *taggableDBImp[T]) FindByTag(ctx context.Context, accountRef, tagRef primitive.ObjectID) ([]T, error) {
// Create filter to find objects with the tag
filter := repository.Filter(model.TagRefsField, tagRef)
// Get all objects with the tag using ListPermissionBound
objects, err := db.dbImp.ListPermissionBound(ctx, filter)
if err != nil {
db.logger.Warn("Failed to get objects with tag", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("tag_ref", tagRef))
return nil, err
}
// Check permissions for all objects using EnforceBatch
db.logger.Debug("Checking permissions for objects with tag", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("tag_ref", tagRef), zap.Int("object_count", len(objects)))
permissions, err := db.enforcer.EnforceBatch(ctx, objects, accountRef, model.ActionRead)
if err != nil {
db.logger.Warn("Failed to check permissions for objects with tag", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("tag_ref", tagRef), zap.Int("object_count", len(objects)))
return nil, merrors.Internal("failed to check permissions for objects with tag")
}
// Filter objects based on permissions and decode them
var results []T
for _, obj := range objects {
objID := *obj.GetID()
if hasPermission, exists := permissions[objID]; exists && hasPermission {
// Decode the object
decodedObj := db.createEmpty()
if err := db.dbImp.Get(ctx, objID, decodedObj); err != nil {
db.logger.Warn("Failed to decode object with tag", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("object_ref", objID), mzap.ObjRef("tag_ref", tagRef))
continue
}
results = append(results, decodedObj)
}
}
db.logger.Debug("Successfully found objects with tag", mzap.ObjRef("account_ref", accountRef),
mzap.ObjRef("tag_ref", tagRef), zap.Int("total_objects", len(objects)), zap.Int("accessible_objects", len(results)))
return results, nil
}
// FindByTags finds all entities that have any of the specified tags with permission checking
func (db *taggableDBImp[T]) FindByTags(ctx context.Context, accountRef primitive.ObjectID, tagRefs []primitive.ObjectID) ([]T, error) {
if len(tagRefs) == 0 {
return []T{}, nil
}
// Convert []primitive.ObjectID to []any for the In method
values := make([]any, len(tagRefs))
for i, tagRef := range tagRefs {
values[i] = tagRef
}
// Create filter to find objects with any of the tags
filter := repository.Query().In(repository.TagRefsField(), values...)
// Get all objects with any of the tags using ListPermissionBound
objects, err := db.dbImp.ListPermissionBound(ctx, filter)
if err != nil {
db.logger.Warn("Failed to get objects with tags", zap.Error(err),
mzap.ObjRef("account_ref", accountRef))
return nil, err
}
// Check permissions for all objects using EnforceBatch
db.logger.Debug("Checking permissions for objects with tags", mzap.ObjRef("account_ref", accountRef),
zap.Int("object_count", len(objects)), zap.Int("tag_count", len(tagRefs)))
permissions, err := db.enforcer.EnforceBatch(ctx, objects, accountRef, model.ActionRead)
if err != nil {
db.logger.Warn("Failed to check permissions for objects with tags", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), zap.Int("object_count", len(objects)))
return nil, merrors.Internal("failed to check permissions for objects with tags")
}
// Filter objects based on permissions and decode them
var results []T
for _, obj := range objects {
objID := *obj.GetID()
if hasPermission, exists := permissions[objID]; exists && hasPermission {
// Decode the object
decodedObj := db.createEmpty()
if err := db.dbImp.Get(ctx, objID, decodedObj); err != nil {
db.logger.Warn("Failed to decode object with tags", zap.Error(err),
mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("object_ref", objID))
continue
}
results = append(results, decodedObj)
}
}
db.logger.Debug("Successfully found objects with tags", mzap.ObjRef("account_ref", accountRef),
zap.Int("total_objects", len(objects)), zap.Int("accessible_objects", len(results)), zap.Int("tag_count", len(tagRefs)))
return results, nil
}