Files
omega-server/local/model/role.go
2025-07-06 19:19:36 +02:00

183 lines
5.1 KiB
Go

package model
import (
"errors"
"strings"
"gorm.io/gorm"
)
// Role represents a role in the system
type Role struct {
BaseModel
Name string `json:"name" gorm:"unique;not null;type:varchar(100)"`
Description string `json:"description" gorm:"type:text"`
Active bool `json:"active" gorm:"default:true"`
System bool `json:"system" gorm:"default:false"` // System roles cannot be deleted
Users []User `json:"-" gorm:"many2many:user_roles;"`
Permissions []Permission `json:"permissions" gorm:"many2many:role_permissions;"`
}
// RoleCreateRequest represents the request to create a new role
type RoleCreateRequest struct {
Name string `json:"name" validate:"required,min=3,max=100"`
Description string `json:"description" validate:"max=500"`
PermissionIDs []string `json:"permissionIds"`
}
// RoleUpdateRequest represents the request to update a role
type RoleUpdateRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,min=3,max=100"`
Description *string `json:"description,omitempty" validate:"omitempty,max=500"`
Active *bool `json:"active,omitempty"`
PermissionIDs []string `json:"permissionIds,omitempty"`
}
// RoleInfo represents public role information
type RoleInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Active bool `json:"active"`
System bool `json:"system"`
Permissions []PermissionInfo `json:"permissions"`
UserCount int64 `json:"userCount"`
CreatedAt string `json:"created_at"`
}
// BeforeCreate is called before creating a role
func (r *Role) BeforeCreate(tx *gorm.DB) error {
r.BaseModel.BeforeCreate()
// Normalize name
r.Name = strings.ToLower(strings.TrimSpace(r.Name))
r.Description = strings.TrimSpace(r.Description)
return r.Validate()
}
// BeforeUpdate is called before updating a role
func (r *Role) BeforeUpdate(tx *gorm.DB) error {
r.BaseModel.BeforeUpdate()
// Normalize fields if they're being updated
if r.Name != "" {
r.Name = strings.ToLower(strings.TrimSpace(r.Name))
}
if r.Description != "" {
r.Description = strings.TrimSpace(r.Description)
}
return r.Validate()
}
// BeforeDelete is called before deleting a role
func (r *Role) BeforeDelete(tx *gorm.DB) error {
if r.System {
return errors.New("system roles cannot be deleted")
}
// Check if role is assigned to any users
var userCount int64
if err := tx.Model(&User{}).Where("roles.id = ?", r.ID).Joins("JOIN user_roles ON users.id = user_roles.user_id").Count(&userCount).Error; err != nil {
return err
}
if userCount > 0 {
return errors.New("cannot delete role that is assigned to users")
}
return nil
}
// Validate validates role data
func (r *Role) Validate() error {
if r.Name == "" {
return errors.New("role name is required")
}
if len(r.Name) < 3 || len(r.Name) > 100 {
return errors.New("role name must be between 3 and 100 characters")
}
if !isValidRoleName(r.Name) {
return errors.New("role name can only contain letters, numbers, underscores, and hyphens")
}
if len(r.Description) > 500 {
return errors.New("role description must not exceed 500 characters")
}
return nil
}
// ToRoleInfo converts Role to RoleInfo (public information)
func (r *Role) ToRoleInfo() RoleInfo {
roleInfo := RoleInfo{
ID: r.ID,
Name: r.Name,
Description: r.Description,
Active: r.Active,
System: r.System,
Permissions: make([]PermissionInfo, len(r.Permissions)),
CreatedAt: r.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
}
// Convert permissions
for i, permission := range r.Permissions {
roleInfo.Permissions[i] = permission.ToPermissionInfo()
}
return roleInfo
}
// HasPermission checks if the role has a specific permission
func (r *Role) HasPermission(permissionName string) bool {
for _, permission := range r.Permissions {
if permission.Name == permissionName {
return true
}
}
return false
}
// AddPermission adds a permission to the role
func (r *Role) AddPermission(permission Permission) {
if !r.HasPermission(permission.Name) {
r.Permissions = append(r.Permissions, permission)
}
}
// RemovePermission removes a permission from the role
func (r *Role) RemovePermission(permissionName string) {
for i, permission := range r.Permissions {
if permission.Name == permissionName {
r.Permissions = append(r.Permissions[:i], r.Permissions[i+1:]...)
break
}
}
}
// GetPermissionNames returns a slice of permission names
func (r *Role) GetPermissionNames() []string {
names := make([]string, len(r.Permissions))
for i, permission := range r.Permissions {
names[i] = permission.Name
}
return names
}
// isValidRoleName validates role name format
func isValidRoleName(name string) bool {
// Allow letters, numbers, underscores, and hyphens
for _, char := range name {
if !((char >= 'a' && char <= 'z') ||
(char >= 'A' && char <= 'Z') ||
(char >= '0' && char <= '9') ||
char == '_' || char == '-') {
return false
}
}
return true
}