183 lines
5.1 KiB
Go
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"`
|
|
DateCreated string `json:"dateCreated"`
|
|
}
|
|
|
|
// 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)),
|
|
DateCreated: r.DateCreated.Format("2006-01-02T15:04:05Z"),
|
|
}
|
|
|
|
// 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
|
|
}
|