package tickets import ( "github.com/leolionad58/ticketbot/internal/config" "github.com/leolionad58/ticketbot/internal/db" ) type Action int const ( ActionAdmin Action = iota ActionClaim ActionClose ActionAdd ActionRemove ActionRename ActionTranscript ActionConvocation ActionReclaim ) type AuthService struct { Config *config.Provider } func NewAuthService(cfg *config.Provider) *AuthService { return &AuthService{Config: cfg} } // CanClose returns true if the user can close the given ticket. // Ticket owner can close if status is 'open' (not yet claimed). Staff/admin can always close. func (a *AuthService) CanClose(memberRoles []string, userID string, ticket *db.Ticket) bool { if ticket == nil { return false } // Owner can close their own unclaimed ticket if userID == ticket.UserID && ticket.Status == "open" { return true } return a.Can(memberRoles, ActionClose, ticket) } // Can returns true if the member (by their role IDs) is authorized to perform action on ticket. // ticket may be nil for actions that don't require a specific ticket context (e.g. ActionAdmin, ActionConvocation). func (a *AuthService) Can(memberRoles []string, action Action, ticket *db.Ticket) bool { cfg := a.Config.Get() hasRole := func(roleID string) bool { for _, r := range memberRoles { if r == roleID { return true } } return false } isAdmin := hasRole(cfg.Bot.AdminRole) staffRoleForTicket := func() string { if ticket == nil { return "" } // Prefer role stored on the ticket itself (populated at open time for DB panels) if ticket.StaffRoleID != "" { return ticket.StaffRoleID } if p, ok := cfg.Panels[ticket.Panel]; ok { if t, ok := p.Types[ticket.Type]; ok { return t.StaffRole } } return "" } switch action { case ActionAdmin: return isAdmin case ActionClaim: if isAdmin { return true } role := staffRoleForTicket() return role != "" && hasRole(role) case ActionClose, ActionAdd, ActionRemove, ActionRename, ActionTranscript, ActionReclaim: if isAdmin { return true } role := staffRoleForTicket() return role != "" && hasRole(role) case ActionConvocation: if isAdmin { return true } for _, panel := range cfg.Panels { for _, t := range panel.Types { if hasRole(t.StaffRole) { return true } } } return false default: return false } }