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