Files
techircd/oper_config.go

340 lines
9.5 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
)
// OperClass defines an operator class with specific permissions
type OperClass struct {
Name string `json:"name"`
Rank int `json:"rank"` // Higher number = higher rank
Description string `json:"description"`
Permissions []string `json:"permissions"`
Inherits string `json:"inherits"` // Inherit permissions from another class
Color string `json:"color"` // Color for display purposes
Symbol string `json:"symbol"` // Symbol to display (*, @, &, etc.)
}
// Oper defines an individual operator
type Oper struct {
Name string `json:"name"`
Password string `json:"password"`
Host string `json:"host"`
Class string `json:"class"`
Flags []string `json:"flags"` // Additional per-user flags
MaxClients int `json:"max_clients"` // Max clients this oper can handle
Expires string `json:"expires"` // Expiration date (optional)
Contact string `json:"contact"` // Contact information
LastSeen string `json:"last_seen"` // Last time this oper was online
}
// RankNames defines custom names for rank levels
type RankNames struct {
Rank1 string `json:"rank_1"` // Default: Helper
Rank2 string `json:"rank_2"` // Default: Moderator
Rank3 string `json:"rank_3"` // Default: Operator
Rank4 string `json:"rank_4"` // Default: Administrator
Rank5 string `json:"rank_5"` // Default: Owner
// Support for custom ranks beyond 5
CustomRanks map[string]int `json:"custom_ranks"` // "CustomName": 6
}
// OperConfig holds the complete operator configuration
type OperConfig struct {
Classes []OperClass `json:"classes"`
Opers []Oper `json:"opers"`
RankNames RankNames `json:"rank_names"`
Settings struct {
RequireSSL bool `json:"require_ssl"`
MaxFailedAttempts int `json:"max_failed_attempts"`
LockoutDuration int `json:"lockout_duration_minutes"`
AllowedCommands []string `json:"allowed_commands"`
LogOperActions bool `json:"log_oper_actions"`
NotifyOnOperUp bool `json:"notify_on_oper_up"`
AutoExpireInactive int `json:"auto_expire_inactive_days"`
RequireTwoFactor bool `json:"require_two_factor"`
} `json:"settings"`
}
// LoadOperConfig loads operator configuration from file
func LoadOperConfig(filename string) (*OperConfig, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read oper config file: %v", err)
}
var config OperConfig
if err := json.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed to parse oper config file: %v", err)
}
return &config, nil
}
// SaveOperConfig saves operator configuration to file
func SaveOperConfig(config *OperConfig, filename string) error {
data, err := json.MarshalIndent(config, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal oper config: %v", err)
}
if err := os.WriteFile(filename, data, 0644); err != nil {
return fmt.Errorf("failed to write oper config file: %v", err)
}
return nil
}
// GetOperClass returns the operator class by name
func (oc *OperConfig) GetOperClass(className string) *OperClass {
for i := range oc.Classes {
if oc.Classes[i].Name == className {
return &oc.Classes[i]
}
}
return nil
}
// GetOper returns the operator by name
func (oc *OperConfig) GetOper(operName string) *Oper {
for i := range oc.Opers {
if oc.Opers[i].Name == operName {
return &oc.Opers[i]
}
}
return nil
}
// GetOperPermissions returns all permissions for an operator (including inherited)
func (oc *OperConfig) GetOperPermissions(operName string) []string {
oper := oc.GetOper(operName)
if oper == nil {
return nil
}
class := oc.GetOperClass(oper.Class)
if class == nil {
return oper.Flags
}
permissions := make(map[string]bool)
// Add class permissions
for _, perm := range class.Permissions {
permissions[perm] = true
}
// Add inherited permissions
if class.Inherits != "" {
inherited := oc.GetOperClass(class.Inherits)
if inherited != nil {
for _, perm := range inherited.Permissions {
permissions[perm] = true
}
}
}
// Add individual flags
for _, flag := range oper.Flags {
permissions[flag] = true
}
// Convert back to slice
result := make([]string, 0, len(permissions))
for perm := range permissions {
result = append(result, perm)
}
return result
}
// GetRankName returns the custom name for a rank level
func (oc *OperConfig) GetRankName(rank int) string {
switch rank {
case 1:
if oc.RankNames.Rank1 != "" {
return oc.RankNames.Rank1
}
return "Helper"
case 2:
if oc.RankNames.Rank2 != "" {
return oc.RankNames.Rank2
}
return "Moderator"
case 3:
if oc.RankNames.Rank3 != "" {
return oc.RankNames.Rank3
}
return "Operator"
case 4:
if oc.RankNames.Rank4 != "" {
return oc.RankNames.Rank4
}
return "Administrator"
case 5:
if oc.RankNames.Rank5 != "" {
return oc.RankNames.Rank5
}
return "Owner"
default:
// Check custom ranks
for name, rankNum := range oc.RankNames.CustomRanks {
if rankNum == rank {
return name
}
}
return fmt.Sprintf("Rank %d", rank)
}
}
// GetOperRankName returns the rank name for an operator
func (oc *OperConfig) GetOperRankName(operName string) string {
oper := oc.GetOper(operName)
if oper == nil {
return "Unknown"
}
class := oc.GetOperClass(oper.Class)
if class == nil {
return "Unknown"
}
return oc.GetRankName(class.Rank)
}
// SetCustomRankName allows adding custom rank names at runtime
func (oc *OperConfig) SetCustomRankName(name string, rank int) {
if oc.RankNames.CustomRanks == nil {
oc.RankNames.CustomRanks = make(map[string]int)
}
oc.RankNames.CustomRanks[name] = rank
}
// HasPermission checks if an operator has a specific permission
func (oc *OperConfig) HasPermission(operName, permission string) bool {
permissions := oc.GetOperPermissions(operName)
for _, perm := range permissions {
if perm == permission || perm == "*" { // * grants all permissions
return true
}
}
return false
}
// GetOperRank returns the rank of an operator
func (oc *OperConfig) GetOperRank(operName string) int {
oper := oc.GetOper(operName)
if oper == nil {
return 0
}
class := oc.GetOperClass(oper.Class)
if class == nil {
return 0
}
return class.Rank
}
// CanOperateOn checks if oper1 can perform actions on oper2 (based on rank)
func (oc *OperConfig) CanOperateOn(oper1Name, oper2Name string) bool {
rank1 := oc.GetOperRank(oper1Name)
rank2 := oc.GetOperRank(oper2Name)
// Higher rank can operate on lower rank
// Same rank cannot operate on each other (unless they have override permission)
return rank1 > rank2 || oc.HasPermission(oper1Name, "override_rank")
}
// DefaultOperConfig returns a default operator configuration
func DefaultOperConfig() *OperConfig {
return &OperConfig{
Classes: []OperClass{
{
Name: "helper",
Rank: 1,
Description: "Helper - Basic moderation commands",
Permissions: []string{"kick", "topic", "mode_channel"},
Color: "green",
Symbol: "%",
},
{
Name: "moderator",
Rank: 2,
Description: "Moderator - Channel and user management",
Permissions: []string{"ban", "unban", "kick", "mute", "topic", "mode_channel", "mode_user", "who_override"},
Inherits: "helper",
Color: "blue",
Symbol: "@",
},
{
Name: "operator",
Rank: 3,
Description: "Operator - Server management commands",
Permissions: []string{"kill", "gline", "rehash", "connect", "squit", "wallops", "operwall"},
Inherits: "moderator",
Color: "red",
Symbol: "*",
},
{
Name: "admin",
Rank: 4,
Description: "Administrator - Full server control",
Permissions: []string{"*"}, // All permissions
Color: "purple",
Symbol: "&",
},
{
Name: "owner",
Rank: 5,
Description: "Server Owner - Ultimate authority",
Permissions: []string{"*", "override_rank", "shutdown", "restart"},
Color: "gold",
Symbol: "~",
},
},
Opers: []Oper{
{
Name: "admin",
Password: "changeme",
Host: "*@localhost",
Class: "admin",
MaxClients: 1000,
Contact: "admin@example.com",
},
},
RankNames: RankNames{
Rank1: "Helper", // Customizable: "Support Staff", "Junior Mod", etc.
Rank2: "Moderator", // Customizable: "Channel Mod", "Guard", etc.
Rank3: "Operator", // Customizable: "IRC Operator", "Senior Staff", etc.
Rank4: "Administrator", // Customizable: "Admin", "Server Admin", etc.
Rank5: "Owner", // Customizable: "Root", "Founder", etc.
CustomRanks: map[string]int{
// Example: "Super Admin": 6,
// Example: "Network Founder": 10,
},
},
Settings: struct {
RequireSSL bool `json:"require_ssl"`
MaxFailedAttempts int `json:"max_failed_attempts"`
LockoutDuration int `json:"lockout_duration_minutes"`
AllowedCommands []string `json:"allowed_commands"`
LogOperActions bool `json:"log_oper_actions"`
NotifyOnOperUp bool `json:"notify_on_oper_up"`
AutoExpireInactive int `json:"auto_expire_inactive_days"`
RequireTwoFactor bool `json:"require_two_factor"`
}{
RequireSSL: false,
MaxFailedAttempts: 3,
LockoutDuration: 30,
AllowedCommands: []string{"OPER", "KILL", "GLINE", "REHASH", "WALLOPS"},
LogOperActions: true,
NotifyOnOperUp: true,
AutoExpireInactive: 365,
RequireTwoFactor: false,
},
}
}