277 lines
7.2 KiB
Go
277 lines
7.2 KiB
Go
package model
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Permission represents a permission in the system
|
|
type Permission struct {
|
|
BaseModel
|
|
Name string `json:"name" gorm:"unique;not null;type:varchar(100)"`
|
|
Description string `json:"description" gorm:"type:text"`
|
|
Category string `json:"category" gorm:"type:varchar(50)"`
|
|
Active bool `json:"active" gorm:"default:true"`
|
|
System bool `json:"system" gorm:"default:false"` // System permissions cannot be deleted
|
|
Roles []Role `json:"-" gorm:"many2many:role_permissions;"`
|
|
}
|
|
|
|
// PermissionCreateRequest represents the request to create a new permission
|
|
type PermissionCreateRequest struct {
|
|
Name string `json:"name" validate:"required,min=3,max=100"`
|
|
Description string `json:"description" validate:"max=500"`
|
|
Category string `json:"category" validate:"required,max=50"`
|
|
}
|
|
|
|
// PermissionUpdateRequest represents the request to update a permission
|
|
type PermissionUpdateRequest struct {
|
|
Name *string `json:"name,omitempty" validate:"omitempty,min=3,max=100"`
|
|
Description *string `json:"description,omitempty" validate:"omitempty,max=500"`
|
|
Category *string `json:"category,omitempty" validate:"omitempty,max=50"`
|
|
Active *bool `json:"active,omitempty"`
|
|
}
|
|
|
|
// PermissionInfo represents public permission information
|
|
type PermissionInfo struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Category string `json:"category"`
|
|
Active bool `json:"active"`
|
|
System bool `json:"system"`
|
|
RoleCount int64 `json:"roleCount"`
|
|
DateCreated string `json:"dateCreated"`
|
|
}
|
|
|
|
// BeforeCreate is called before creating a permission
|
|
func (p *Permission) BeforeCreate(tx *gorm.DB) error {
|
|
p.BaseModel.BeforeCreate()
|
|
|
|
// Normalize fields
|
|
p.Name = strings.ToLower(strings.TrimSpace(p.Name))
|
|
p.Description = strings.TrimSpace(p.Description)
|
|
p.Category = strings.ToLower(strings.TrimSpace(p.Category))
|
|
|
|
return p.Validate()
|
|
}
|
|
|
|
// BeforeUpdate is called before updating a permission
|
|
func (p *Permission) BeforeUpdate(tx *gorm.DB) error {
|
|
p.BaseModel.BeforeUpdate()
|
|
|
|
// Normalize fields if they're being updated
|
|
if p.Name != "" {
|
|
p.Name = strings.ToLower(strings.TrimSpace(p.Name))
|
|
}
|
|
if p.Description != "" {
|
|
p.Description = strings.TrimSpace(p.Description)
|
|
}
|
|
if p.Category != "" {
|
|
p.Category = strings.ToLower(strings.TrimSpace(p.Category))
|
|
}
|
|
|
|
return p.Validate()
|
|
}
|
|
|
|
// BeforeDelete is called before deleting a permission
|
|
func (p *Permission) BeforeDelete(tx *gorm.DB) error {
|
|
if p.System {
|
|
return errors.New("system permissions cannot be deleted")
|
|
}
|
|
|
|
// Check if permission is assigned to any roles
|
|
var roleCount int64
|
|
if err := tx.Model(&Role{}).Where("permissions.id = ?", p.ID).Joins("JOIN role_permissions ON roles.id = role_permissions.role_id").Count(&roleCount).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if roleCount > 0 {
|
|
return errors.New("cannot delete permission that is assigned to roles")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Validate validates permission data
|
|
func (p *Permission) Validate() error {
|
|
if p.Name == "" {
|
|
return errors.New("permission name is required")
|
|
}
|
|
|
|
if len(p.Name) < 3 || len(p.Name) > 100 {
|
|
return errors.New("permission name must be between 3 and 100 characters")
|
|
}
|
|
|
|
if !isValidPermissionName(p.Name) {
|
|
return errors.New("permission name must follow the format 'resource:action' (e.g., 'user:create')")
|
|
}
|
|
|
|
if p.Category == "" {
|
|
return errors.New("permission category is required")
|
|
}
|
|
|
|
if len(p.Category) > 50 {
|
|
return errors.New("permission category must not exceed 50 characters")
|
|
}
|
|
|
|
if len(p.Description) > 500 {
|
|
return errors.New("permission description must not exceed 500 characters")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ToPermissionInfo converts Permission to PermissionInfo (public information)
|
|
func (p *Permission) ToPermissionInfo() PermissionInfo {
|
|
return PermissionInfo{
|
|
ID: p.ID,
|
|
Name: p.Name,
|
|
Description: p.Description,
|
|
Category: p.Category,
|
|
Active: p.Active,
|
|
System: p.System,
|
|
DateCreated: p.DateCreated.Format("2006-01-02T15:04:05Z"),
|
|
}
|
|
}
|
|
|
|
// GetResource extracts the resource part from a permission name (e.g., "user:create" -> "user")
|
|
func (p *Permission) GetResource() string {
|
|
parts := strings.Split(p.Name, ":")
|
|
if len(parts) > 0 {
|
|
return parts[0]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetAction extracts the action part from a permission name (e.g., "user:create" -> "create")
|
|
func (p *Permission) GetAction() string {
|
|
parts := strings.Split(p.Name, ":")
|
|
if len(parts) > 1 {
|
|
return parts[1]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// isValidPermissionName validates permission name format
|
|
func isValidPermissionName(name string) bool {
|
|
// Permission names should follow the format "resource:action"
|
|
parts := strings.Split(name, ":")
|
|
if len(parts) != 2 {
|
|
return false
|
|
}
|
|
|
|
resource := parts[0]
|
|
action := parts[1]
|
|
|
|
// Validate resource part
|
|
if len(resource) < 2 || len(resource) > 50 {
|
|
return false
|
|
}
|
|
|
|
// Validate action part
|
|
if len(action) < 2 || len(action) > 50 {
|
|
return false
|
|
}
|
|
|
|
// Check if both parts contain only valid characters
|
|
return isValidIdentifier(resource) && isValidIdentifier(action)
|
|
}
|
|
|
|
// isValidIdentifier checks if a string is a valid identifier (letters, numbers, underscores)
|
|
func isValidIdentifier(str string) bool {
|
|
for _, char := range str {
|
|
if !((char >= 'a' && char <= 'z') ||
|
|
(char >= 'A' && char <= 'Z') ||
|
|
(char >= '0' && char <= '9') ||
|
|
char == '_') {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Common permission categories
|
|
const (
|
|
PermissionCategoryUser = "user"
|
|
PermissionCategoryRole = "role"
|
|
PermissionCategorySystem = "system"
|
|
PermissionCategoryContent = "content"
|
|
PermissionCategoryReport = "report"
|
|
)
|
|
|
|
// Common permission patterns
|
|
const (
|
|
PermissionCreate = "create"
|
|
PermissionRead = "read"
|
|
PermissionUpdate = "update"
|
|
PermissionDelete = "delete"
|
|
PermissionManage = "manage"
|
|
PermissionAdmin = "admin"
|
|
)
|
|
|
|
// GetStandardPermissions returns a list of standard permissions for a resource
|
|
func GetStandardPermissions(resource string) []Permission {
|
|
return []Permission{
|
|
{
|
|
Name: resource + ":" + PermissionCreate,
|
|
Description: "Create new " + resource + " records",
|
|
Category: resource,
|
|
Active: true,
|
|
},
|
|
{
|
|
Name: resource + ":" + PermissionRead,
|
|
Description: "Read " + resource + " records",
|
|
Category: resource,
|
|
Active: true,
|
|
},
|
|
{
|
|
Name: resource + ":" + PermissionUpdate,
|
|
Description: "Update " + resource + " records",
|
|
Category: resource,
|
|
Active: true,
|
|
},
|
|
{
|
|
Name: resource + ":" + PermissionDelete,
|
|
Description: "Delete " + resource + " records",
|
|
Category: resource,
|
|
Active: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Common system permissions
|
|
const (
|
|
ServerView = "server:view"
|
|
ServerUpdate = "server:update"
|
|
ServerStart = "server:start"
|
|
ServerStop = "server:stop"
|
|
ConfigView = "config:view"
|
|
ConfigUpdate = "config:update"
|
|
)
|
|
|
|
// AllPermissions returns all available permissions in the system
|
|
var AllPermissions = []string{
|
|
"user:create",
|
|
"user:read",
|
|
"user:update",
|
|
"user:delete",
|
|
"role:create",
|
|
"role:read",
|
|
"role:update",
|
|
"role:delete",
|
|
"permission:create",
|
|
"permission:read",
|
|
"permission:update",
|
|
"permission:delete",
|
|
ServerView,
|
|
ServerUpdate,
|
|
ServerStart,
|
|
ServerStop,
|
|
ConfigView,
|
|
ConfigUpdate,
|
|
"audit:read",
|
|
"system:admin",
|
|
}
|