package tools import ( "encoding/json" "fmt" "slices" ) // teamAccessSettings defines access control rules stored in agent_teams.settings JSONB. // Empty/nil lists mean "no restriction". Deny lists take precedence over allow lists. type teamAccessSettings struct { Version *int `json:"version,omitempty"` AllowUserIDs []string `json:"allow_user_ids"` DenyUserIDs []string `json:"deny_user_ids"` AllowChannels []string `json:"allow_channels"` DenyChannels []string `json:"deny_channels"` Notifications *TeamNotifyConfig `json:"notifications,omitempty"` FollowupIntervalMins *int `json:"followup_interval_minutes,omitempty"` FollowupMaxReminders *int `json:"followup_max_reminders,omitempty"` EscalationMode string `json:"escalation_mode,omitempty"` EscalationActions []string `json:"escalation_actions,omitempty"` } // checkTeamAccess validates whether a user/channel combination is authorized // for team operations. Returns nil if access is allowed. // System channels (ChannelTeammate, ChannelSystem) always pass. // Empty settings = open access (no restrictions). func checkTeamAccess(settings json.RawMessage, userID, channel string) error { if len(settings) == 0 || string(settings) == "{}" { return nil } var s teamAccessSettings if json.Unmarshal(settings, &s) != nil { return nil // malformed = fail open } // System/internal access always allowed if channel == ChannelTeammate || channel == ChannelSystem { return nil } // User check: deny > allow if userID != "" { if slices.Contains(s.DenyUserIDs, userID) { return fmt.Errorf("user not authorized for this team") } if len(s.AllowUserIDs) > 0 { found := slices.Contains(s.AllowUserIDs, userID) if !found { return fmt.Errorf("user not authorized for this team") } } } // Channel check: deny > allow if channel != "" { if slices.Contains(s.DenyChannels, channel) { return fmt.Errorf("channel %q not authorized for this team", channel) } if len(s.AllowChannels) > 0 { found := slices.Contains(s.AllowChannels, channel) if !found { return fmt.Errorf("channel %q not authorized for this team", channel) } } } return nil }