Files
goclaw/internal/tools/team_access_policy.go
T
viettranx 49441f7305 refactor: remove dead delegate code, rename lane/channel to team/teammate
- Remove handleDelegateAnnounce() dead code (no sender emits delegate:* messages)
- Remove delegate tool reference from intent_classify.go
- Rename LaneDelegate → LaneTeam with backward-compat env var fallback
- Rename ChannelDelegate → ChannelTeammate across all team tool files
- Comment out lifecycle guards in team_tasks_lifecycle.go (TODO: reviewer workflow)
- Update string literals in cron.go, task_ticker.go
- Gate tool_status placeholder_update to non-streaming runs only
- Skip FinalizeStream on tool.call to prevent mid-run content loss
2026-03-18 11:04:45 +07:00

70 lines
2.2 KiB
Go

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
}