143 lines
5.3 KiB
Go
143 lines
5.3 KiB
Go
package native
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/tech/sendico/pkg/auth/internal/native/nstructures"
|
|
"github.com/tech/sendico/pkg/db/role"
|
|
"github.com/tech/sendico/pkg/db/storable"
|
|
"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"
|
|
)
|
|
|
|
// RoleManager manages roles using Casbin.
|
|
type RoleManager struct {
|
|
logger mlogger.Logger
|
|
enforcer *Enforcer
|
|
rdb role.DB
|
|
rolePermissionRef primitive.ObjectID
|
|
}
|
|
|
|
// NewRoleManager creates a new RoleManager.
|
|
func NewRoleManager(logger mlogger.Logger, enforcer *Enforcer, rolePermissionRef primitive.ObjectID, rdb role.DB) *RoleManager {
|
|
return &RoleManager{
|
|
logger: logger.Named("role"),
|
|
enforcer: enforcer,
|
|
rdb: rdb,
|
|
rolePermissionRef: rolePermissionRef,
|
|
}
|
|
}
|
|
|
|
// validateObjectIDs ensures that all provided ObjectIDs are non-zero.
|
|
func (rm *RoleManager) validateObjectIDs(ids ...primitive.ObjectID) error {
|
|
for _, id := range ids {
|
|
if id.IsZero() {
|
|
return merrors.InvalidArgument("Object references cannot be zero")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// fetchRolesFromPolicies retrieves and converts policies to roles.
|
|
func (rm *RoleManager) fetchRolesFromPolicies(roles []nstructures.RoleAssignment, organizationRef primitive.ObjectID) []model.RoleDescription {
|
|
result := make([]model.RoleDescription, len(roles))
|
|
for i, role := range roles {
|
|
result[i] = model.RoleDescription{
|
|
Base: storable.Base{ID: *role.GetID()},
|
|
OrganizationRef: organizationRef,
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Create creates a new role in an organization.
|
|
func (rm *RoleManager) Create(ctx context.Context, organizationRef primitive.ObjectID, description *model.Describable) (*model.RoleDescription, error) {
|
|
if err := rm.validateObjectIDs(organizationRef); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
role := &model.RoleDescription{
|
|
OrganizationRef: organizationRef,
|
|
Describable: *description,
|
|
}
|
|
if err := rm.rdb.Create(ctx, role); err != nil {
|
|
rm.logger.Warn("Failed to create role", zap.Error(err), mzap.ObjRef("organization_ref", organizationRef))
|
|
return nil, err
|
|
}
|
|
|
|
rm.logger.Info("Role created successfully", mzap.StorableRef(role), mzap.ObjRef("organization_ref", organizationRef))
|
|
return role, nil
|
|
}
|
|
|
|
// Assign assigns a role to a user in the given organization.
|
|
func (rm *RoleManager) Assign(ctx context.Context, role *model.Role) error {
|
|
if err := rm.validateObjectIDs(role.DescriptionRef, role.AccountRef, role.OrganizationRef); err != nil {
|
|
return err
|
|
}
|
|
assogment := nstructures.RoleAssignment{Role: *role}
|
|
err := rm.enforcer.rdb.Create(ctx, &assogment)
|
|
return rm.logPolicyResult("assign", err == nil, err, role.DescriptionRef, role.AccountRef, role.OrganizationRef)
|
|
}
|
|
|
|
// Delete removes a role entirely and cleans up associated Casbin policies.
|
|
func (rm *RoleManager) Delete(ctx context.Context, roleRef primitive.ObjectID) error {
|
|
if err := rm.validateObjectIDs(roleRef); err != nil {
|
|
rm.logger.Warn("Failed to delete role", mzap.ObjRef("role_ref", roleRef))
|
|
return err
|
|
}
|
|
|
|
if err := rm.rdb.Delete(ctx, roleRef); err != nil {
|
|
rm.logger.Warn("Failed to delete role", mzap.ObjRef("role_ref", roleRef))
|
|
return err
|
|
}
|
|
|
|
if err := rm.enforcer.rdb.DeleteRole(ctx, roleRef); err != nil {
|
|
rm.logger.Warn("Failed to remove role", zap.Error(err), mzap.ObjRef("role_ref", roleRef))
|
|
return err
|
|
}
|
|
|
|
rm.logger.Info("Role deleted successfully along with associated policies", mzap.ObjRef("role_ref", roleRef))
|
|
return nil
|
|
}
|
|
|
|
// Revoke removes a role from a user.
|
|
func (rm *RoleManager) Revoke(ctx context.Context, roleRef, accountRef, organizationRef primitive.ObjectID) error {
|
|
if err := rm.validateObjectIDs(roleRef, accountRef, organizationRef); err != nil {
|
|
return err
|
|
}
|
|
|
|
err := rm.enforcer.rdb.RemoveRole(ctx, roleRef, organizationRef, accountRef)
|
|
return rm.logPolicyResult("revoke", err == nil, err, roleRef, accountRef, organizationRef)
|
|
}
|
|
|
|
// logPolicyResult logs results for Assign and Revoke.
|
|
func (rm *RoleManager) logPolicyResult(action string, result bool, err error, roleRef, accountRef, organizationRef primitive.ObjectID) error {
|
|
if err != nil {
|
|
rm.logger.Warn("Failed to "+action+" role", zap.Error(err), mzap.ObjRef("role_ref", roleRef), mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("organization_ref", organizationRef))
|
|
return err
|
|
}
|
|
msg := "Role " + action + "ed successfully"
|
|
if !result {
|
|
msg = "Role already " + action + "ed"
|
|
}
|
|
rm.logger.Info(msg, mzap.ObjRef("role_ref", roleRef), mzap.ObjRef("account_ref", accountRef), mzap.ObjRef("organization_ref", organizationRef))
|
|
return nil
|
|
}
|
|
|
|
// List retrieves all roles in an organization or all roles if organizationRef is zero.
|
|
func (rm *RoleManager) List(ctx context.Context, organizationRef primitive.ObjectID) ([]model.RoleDescription, error) {
|
|
roles4Venues, err := rm.enforcer.rdb.RolesForVenue(ctx, organizationRef)
|
|
if err != nil {
|
|
rm.logger.Warn("Failed to fetch grouping policies", zap.Error(err), mzap.ObjRef("organization_ref", organizationRef))
|
|
return nil, err
|
|
}
|
|
|
|
roles := rm.fetchRolesFromPolicies(roles4Venues, organizationRef)
|
|
rm.logger.Info("Retrieved roles for organization", mzap.ObjRef("organization_ref", organizationRef), zap.Int("count", len(roles)))
|
|
return roles, nil
|
|
}
|