Files
sendico/api/pkg/auth/internal/casbin/permissions.go
Stephan D 62a6631b9a
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
service backend
2025-11-07 18:35:26 +01:00

168 lines
5.4 KiB
Go

package casbin
import (
"context"
"github.com/tech/sendico/pkg/auth/anyobject"
"github.com/tech/sendico/pkg/auth/internal/casbin/serialization"
"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"
)
// CasbinPermissionManager manages permissions using Casbin.
type CasbinPermissionManager struct {
logger mlogger.Logger // Logger for logging operations
enforcer *CasbinEnforcer // Casbin enforcer for managing policies
serializer serialization.Policy // Serializer for converting policies to/from Casbin
}
// GrantToRole adds a permission to a role in Casbin.
func (m *CasbinPermissionManager) GrantToRole(ctx context.Context, policy *model.RolePolicy) error {
objRef := anyobject.ID
if (policy.ObjectRef != nil) && (*policy.ObjectRef != primitive.NilObjectID) {
objRef = policy.ObjectRef.Hex()
}
m.logger.Debug("Granting permission to role",
mzap.ObjRef("role_ref", policy.RoleDescriptionRef),
mzap.ObjRef("permission_ref", policy.DescriptionRef),
zap.String("object_ref", objRef),
zap.String("action", string(policy.Effect.Action)),
zap.String("effect", string(policy.Effect.Effect)),
)
// Serialize permission
serializedPolicy, err := m.serializer.Serialize(policy)
if err != nil {
m.logger.Error("Failed to serialize permission while granting permission", zap.Error(err),
mzap.ObjRef("role_ref", policy.RoleDescriptionRef),
mzap.ObjRef("permission_ref", policy.DescriptionRef),
mzap.ObjRef("organization_ref", policy.OrganizationRef),
)
return err
}
// Add policy to Casbin
added, err := m.enforcer.enforcer.AddPolicy(serializedPolicy...)
if err != nil {
m.logger.Error("Failed to add policy to Casbin", zap.Error(err))
return err
}
if added {
m.logger.Info("Policy added to Casbin",
mzap.ObjRef("role_ref", policy.RoleDescriptionRef),
mzap.ObjRef("permission_ref", policy.DescriptionRef),
zap.String("object_ref", objRef),
)
} else {
m.logger.Warn("Policy already exists in Casbin",
mzap.ObjRef("role_ref", policy.RoleDescriptionRef),
mzap.ObjRef("permission_ref", policy.DescriptionRef),
zap.String("object_ref", objRef),
)
}
return nil
}
// RevokeFromRole removes a permission from a role in Casbin.
func (m *CasbinPermissionManager) RevokeFromRole(ctx context.Context, policy *model.RolePolicy) error {
objRef := anyobject.ID
if policy.ObjectRef != nil {
objRef = policy.ObjectRef.Hex()
}
m.logger.Debug("Revoking permission from role",
mzap.ObjRef("role_ref", policy.RoleDescriptionRef),
mzap.ObjRef("permission_ref", policy.DescriptionRef),
zap.String("object_ref", objRef),
zap.String("action", string(policy.Effect.Action)),
zap.String("effect", string(policy.Effect.Effect)),
)
// Serialize policy
serializedPolicy, err := m.serializer.Serialize(policy)
if err != nil {
m.logger.Error("Failed to serialize policy while revoking permission from role",
zap.Error(err), mzap.ObjRef("role_ref", policy.RoleDescriptionRef),
mzap.ObjRef("policy_ref", policy.DescriptionRef))
return err
}
// Remove policy from Casbin
removed, err := m.enforcer.enforcer.RemovePolicy(serializedPolicy...)
if err != nil {
m.logger.Error("Failed to remove policy from Casbin", zap.Error(err))
return err
}
if removed {
m.logger.Info("Policy removed from Casbin",
mzap.ObjRef("role_ref", policy.RoleDescriptionRef),
mzap.ObjRef("permission_ref", policy.DescriptionRef),
zap.String("object_ref", objRef),
)
} else {
m.logger.Warn("Policy does not exist in Casbin",
mzap.ObjRef("role_ref", policy.RoleDescriptionRef),
mzap.ObjRef("permission_ref", policy.DescriptionRef),
zap.String("object_ref", objRef),
)
}
return nil
}
// GetPolicies retrieves all policies for a specific role.
func (m *CasbinPermissionManager) GetPolicies(
ctx context.Context,
roleRef primitive.ObjectID,
) ([]model.RolePolicy, error) {
m.logger.Debug("Fetching policies for role", mzap.ObjRef("role_ref", roleRef))
// Retrieve Casbin policies for the role
policies, err := m.enforcer.enforcer.GetFilteredPolicy(0, roleRef.Hex())
if err != nil {
m.logger.Warn("Failed to get policies", zap.Error(err), mzap.ObjRef("role_ref", roleRef))
return nil, err
}
if len(policies) == 0 {
m.logger.Info("No policies found for role", mzap.ObjRef("role_ref", roleRef))
return nil, merrors.NoData("no policies")
}
// Deserialize policies
var result []model.RolePolicy
for _, policy := range policies {
permission, err := m.serializer.Deserialize(policy)
if err != nil {
m.logger.Warn("Failed to deserialize policy", zap.Error(err), zap.String("policy", policy[0]))
continue
}
result = append(result, *permission)
}
m.logger.Debug("Policies fetched successfully", mzap.ObjRef("role_ref", roleRef), zap.Int("count", len(result)))
return result, nil
}
// Save persists changes to the Casbin policy store.
func (m *CasbinPermissionManager) Save() error {
if err := m.enforcer.enforcer.SavePolicy(); err != nil {
m.logger.Error("Failed to save policies in Casbin", zap.Error(err))
return err
}
m.logger.Info("Policies successfully saved in Casbin")
return nil
}
func NewPermissionManager(logger mlogger.Logger, enforcer *CasbinEnforcer) *CasbinPermissionManager {
return &CasbinPermissionManager{
logger: logger.Named("permission"),
enforcer: enforcer,
serializer: serialization.NewPolicySerializer(),
}
}