package models import ( "encoding/json" "fmt" "strconv" "time" ) // Permission represents a system permission type Permission struct { ID int `json:"id" db:"id"` PermissionName string `json:"permission_name" db:"permission_name"` Description string `json:"description" db:"description"` Resource string `json:"resource" db:"resource"` Action string `json:"action" db:"action"` } // RolePermission represents the junction table linking roles to permissions type RolePermission struct { ID int `json:"id" db:"id"` RoleID int `json:"role_id" db:"role_id"` PermissionID int `json:"permission_id" db:"permission_id"` } // PolicyAttribute represents an ABAC policy attribute/constraint type PolicyAttribute struct { ID int `json:"id" db:"id"` AttributeName string `json:"attribute_name" db:"attribute_name"` AttributeType string `json:"attribute_type" db:"attribute_type"` // user, resource, environment Comparison string `json:"comparison" db:"comparison"` // =, !=, >, <, >=, <=, IN, CONTAINS AttributeValue string `json:"attribute_value" db:"attribute_value"` PermissionID int `json:"permission_id" db:"permission_id"` } // UserAttribute represents user-specific attributes for ABAC type UserAttribute struct { ID int `json:"id" db:"id"` UserID string `json:"user_id" db:"user_id"` AttributeName string `json:"attribute_name" db:"attribute_name"` AttributeValue string `json:"attribute_value" db:"attribute_value"` } // User represents a system user type User struct { UsersID string `json:"users_id" db:"user_id"` FirstName string `json:"first_name" db:"first_name"` MiddleInitial string `json:"middle_initial" db:"middle_initial"` LastName string `json:"last_name" db:"last_name"` Suffix string `json:"suffix" db:"suffix"` EmailAddress string `json:"email_address" db:"email_address"` HomeAddress string `json:"home_address" db:"home_address"` ContactNumber string `json:"contact_number" db:"contact_number"` RoleID int `json:"role_id" db:"role_id"` IsDeleted string `json:"is_deleted" db:"is_deleted"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // AuthorizationContext holds all context needed for authorization decisions type AuthorizationContext struct { UsersID string `json:"users_id"` Resource string `json:"resource"` Action string `json:"action"` RoleID int `json:"role_id"` // User's role ID RoleIDs []int `json:"-"` CandidateRoles []int `json:"-"` UserAttributes map[string]string `json:"user_attributes"` ResourceData map[string]string `json:"resource_data"` // Additional resource context Environment map[string]string `json:"environment"` // Time, location, etc. } // UnmarshalJSON handles role_id as either string or int func (ac *AuthorizationContext) UnmarshalJSON(data []byte) error { type Alias AuthorizationContext aux := &struct { RoleIDRaw json.RawMessage `json:"role_id"` *Alias }{ Alias: (*Alias)(ac), } if err := json.Unmarshal(data, &aux); err != nil { return err } // Handle role_id as string, int, or array if len(aux.RoleIDRaw) > 0 { // Try unmarshaling as int first var roleInt int if err := json.Unmarshal(aux.RoleIDRaw, &roleInt); err == nil { ac.RoleID = roleInt ac.RoleIDs = []int{roleInt} } else { // Try as array of ints (take first element) var roleArray []int if err := json.Unmarshal(aux.RoleIDRaw, &roleArray); err == nil { if len(roleArray) > 0 { ac.RoleID = roleArray[0] ac.RoleIDs = roleArray } } else { // Try as string var roleStr string if err := json.Unmarshal(aux.RoleIDRaw, &roleStr); err == nil { if roleStr != "" { var convErr error ac.RoleID, convErr = strconv.Atoi(roleStr) if convErr != nil { return fmt.Errorf("invalid role_id: %s", roleStr) } ac.RoleIDs = []int{ac.RoleID} } } else { return fmt.Errorf("role_id must be a number, numeric string, or array of numbers") } } } } return nil } // AuthorizationResult contains the result of an authorization check type AuthorizationResult struct { Allowed bool `json:"allowed"` RedirectRoute string `json:"redirect_route,omitempty"` // Optional redirect route Message string `json:"message,omitempty"` // Optional message } // CachedAuthorizationService adds caching layer to authorization type CachedAuthorizationService struct { PermissionCache map[string]*Permission `json:"-"` // key: "resource:action" PolicyCache map[int][]PolicyAttribute `json:"-"` UserAttrCache map[string]map[string]string `json:"-"` // key: userID CacheMutex interface{} `json:"-"` // sync.RWMutex UserAttrMutex interface{} `json:"-"` // sync.RWMutex CacheExpiry time.Duration `json:"cache_expiry"` LastCacheRefresh time.Time `json:"last_cache_refresh"` }