merge: issue/0024c-security-integration — security/ YAML files wired to launcher + runtime
Conecta el sistema centralizado de grupos y permisos al launcher y runtime. Elimina controles per-agente obsoletos. Activa feature flag. Docs actualizados.
This commit is contained in:
@@ -57,6 +57,7 @@ agents/runtime.go Agent{}: ensambla core + shell
|
|||||||
agents/<id>/ agent.go (reglas puras) + config.yaml + prompts/system.md
|
agents/<id>/ agent.go (reglas puras) + config.yaml + prompts/system.md
|
||||||
tools/ tool registry + tool implementations (subpackages)
|
tools/ tool registry + tool implementations (subpackages)
|
||||||
internal/config/ schema.go + loader.go
|
internal/config/ schema.go + loader.go
|
||||||
|
security/ grupos de usuarios/agentes + politicas de permisos (YAMLs)
|
||||||
cmd/launcher/ entrypoint principal (rulesRegistry)
|
cmd/launcher/ entrypoint principal (rulesRegistry)
|
||||||
cmd/agentctl/ CLI de gestion
|
cmd/agentctl/ CLI de gestion
|
||||||
crons/ catálogo de automatizaciones nombradas (schedule.yaml + prompts)
|
crons/ catálogo de automatizaciones nombradas (schedule.yaml + prompts)
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ matrix:
|
|||||||
dm_respond: true
|
dm_respond: true
|
||||||
ignore_bots: true
|
ignore_bots: true
|
||||||
ignore_users: []
|
ignore_users: []
|
||||||
allowed_users: [] # vacío = sin restricción (todos pueden hablar)
|
|
||||||
unauthorized_response: silent # silent | explicit
|
unauthorized_response: silent # silent | explicit
|
||||||
min_power_level: 0
|
min_power_level: 0
|
||||||
|
|
||||||
@@ -208,14 +207,6 @@ ssh:
|
|||||||
# PERMISOS Y SEGURIDAD
|
# PERMISOS Y SEGURIDAD
|
||||||
# ============================================
|
# ============================================
|
||||||
security:
|
security:
|
||||||
roles:
|
|
||||||
admin:
|
|
||||||
users: ["@admin:matrix-af2f3d.organic-machine.com"]
|
|
||||||
actions: ["*"]
|
|
||||||
user:
|
|
||||||
users: ["*"]
|
|
||||||
actions: ["*"]
|
|
||||||
|
|
||||||
audit:
|
audit:
|
||||||
enabled: false
|
enabled: false
|
||||||
log_file: "./agents/asistente-2/data/audit.log"
|
log_file: "./agents/asistente-2/data/audit.log"
|
||||||
|
|||||||
@@ -162,10 +162,6 @@ matrix:
|
|||||||
dm_respond: true # responde en DMs (modo principal por ahora)
|
dm_respond: true # responde en DMs (modo principal por ahora)
|
||||||
ignore_bots: true
|
ignore_bots: true
|
||||||
ignore_users: []
|
ignore_users: []
|
||||||
allowed_users: [] # vacío = sin restricción (todos pueden hablar)
|
|
||||||
# allowed_users: # ejemplo con restricción:
|
|
||||||
# - "@admin:matrix-af2f3d.organic-machine.com"
|
|
||||||
# - "@enmanuel:matrix-af2f3d.organic-machine.com"
|
|
||||||
unauthorized_response: silent # silent | explicit
|
unauthorized_response: silent # silent | explicit
|
||||||
min_power_level: 0 # cualquiera puede hablar con el assistant
|
min_power_level: 0 # cualquiera puede hablar con el assistant
|
||||||
|
|
||||||
@@ -208,14 +204,6 @@ ssh:
|
|||||||
# PERMISOS Y SEGURIDAD
|
# PERMISOS Y SEGURIDAD
|
||||||
# ============================================
|
# ============================================
|
||||||
security:
|
security:
|
||||||
roles:
|
|
||||||
admin:
|
|
||||||
users: ["@admin:matrix-af2f3d.organic-machine.com"]
|
|
||||||
actions: ["*"]
|
|
||||||
user:
|
|
||||||
users: ["*"]
|
|
||||||
actions: ["*"]
|
|
||||||
|
|
||||||
audit:
|
audit:
|
||||||
enabled: false
|
enabled: false
|
||||||
log_file: "./agents/assistant-bot/data/audit.log"
|
log_file: "./agents/assistant-bot/data/audit.log"
|
||||||
|
|||||||
+5
-9
@@ -115,8 +115,10 @@ func (a *Agent) ClearWindow(roomID string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New assembles an Agent from its config, rules, and logger.
|
// New assembles an Agent from its config, rules, pre-resolved ACL, and logger.
|
||||||
func New(cfg *config.AgentConfig, rules []decision.Rule, logger *slog.Logger) (*Agent, error) {
|
// The ACL is resolved externally (e.g. from security/ YAML files) and injected here.
|
||||||
|
// Pass acl.ACL{} (empty) for open access (no restrictions).
|
||||||
|
func New(cfg *config.AgentConfig, rules []decision.Rule, agentACL acl.ACL, logger *slog.Logger) (*Agent, error) {
|
||||||
// Matrix client
|
// Matrix client
|
||||||
matrixClient, err := matrix.New(cfg.Matrix)
|
matrixClient, err := matrix.New(cfg.Matrix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -230,14 +232,8 @@ func New(cfg *config.AgentConfig, rules []decision.Rule, logger *slog.Logger) (*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build ACL from security roles config
|
|
||||||
aclRoles := make(map[string]acl.RoleDef, len(cfg.Security.Roles))
|
|
||||||
for name, r := range cfg.Security.Roles {
|
|
||||||
aclRoles[name] = acl.RoleDef{Users: r.Users, Actions: r.Actions}
|
|
||||||
}
|
|
||||||
agentACL := acl.FromMap(aclRoles)
|
|
||||||
if !agentACL.Empty() {
|
if !agentACL.Empty() {
|
||||||
logger.Info("acl enabled", "roles", len(cfg.Security.Roles))
|
logger.Info("acl enabled (centralized security policy)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tool registry — register tools enabled in config
|
// Tool registry — register tools enabled in config
|
||||||
|
|||||||
+29
-6
@@ -26,9 +26,11 @@ import (
|
|||||||
"github.com/enmanuel/agents/internal/config"
|
"github.com/enmanuel/agents/internal/config"
|
||||||
"github.com/enmanuel/agents/pkg/decision"
|
"github.com/enmanuel/agents/pkg/decision"
|
||||||
"github.com/enmanuel/agents/pkg/orchestration"
|
"github.com/enmanuel/agents/pkg/orchestration"
|
||||||
|
pksecurity "github.com/enmanuel/agents/pkg/security"
|
||||||
"github.com/enmanuel/agents/shell/bus"
|
"github.com/enmanuel/agents/shell/bus"
|
||||||
agentlog "github.com/enmanuel/agents/shell/logger"
|
agentlog "github.com/enmanuel/agents/shell/logger"
|
||||||
orchshell "github.com/enmanuel/agents/shell/orchestration"
|
orchshell "github.com/enmanuel/agents/shell/orchestration"
|
||||||
|
shellsecurity "github.com/enmanuel/agents/shell/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rulesRegistry maps agent IDs to their rule factories.
|
// rulesRegistry maps agent IDs to their rule factories.
|
||||||
@@ -81,6 +83,19 @@ func main() {
|
|||||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
|
// ── Load centralized security policy ──
|
||||||
|
secPolicy, secErr := shellsecurity.Load("security/")
|
||||||
|
if secErr != nil {
|
||||||
|
logger.Warn("security policy load failed, using empty policy (open access)", "err", secErr)
|
||||||
|
secPolicy = pksecurity.SecurityPolicy{}
|
||||||
|
} else {
|
||||||
|
logger.Info("security policy loaded",
|
||||||
|
"user_groups", len(secPolicy.UserGroups),
|
||||||
|
"agent_groups", len(secPolicy.AgentGroups),
|
||||||
|
"policies", len(secPolicy.Policies),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// ── Shared bus for inter-agent communication ──
|
// ── Shared bus for inter-agent communication ──
|
||||||
agentBus := bus.New(logger)
|
agentBus := bus.New(logger)
|
||||||
|
|
||||||
@@ -95,11 +110,12 @@ func main() {
|
|||||||
|
|
||||||
// ── Shared dependencies for agent registry ──
|
// ── Shared dependencies for agent registry ──
|
||||||
deps := &launchDeps{
|
deps := &launchDeps{
|
||||||
agentBus: agentBus,
|
agentBus: agentBus,
|
||||||
orch: orch,
|
orch: orch,
|
||||||
logDir: logDir,
|
logDir: logDir,
|
||||||
logLevel: lvl,
|
logLevel: lvl,
|
||||||
parentCtx: ctx,
|
parentCtx: ctx,
|
||||||
|
secPolicy: secPolicy,
|
||||||
}
|
}
|
||||||
registry := newAgentRegistry(deps)
|
registry := newAgentRegistry(deps)
|
||||||
|
|
||||||
@@ -158,7 +174,14 @@ func main() {
|
|||||||
agentCleanup = func() {}
|
agentCleanup = func() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := agents.New(cfg, rules, agentLogger)
|
// Resolve centralized ACL for this agent
|
||||||
|
agentACL := pksecurity.ResolveACL(cfg.Agent.ID, deps.secPolicy)
|
||||||
|
agentLogger.Debug("resolved acl for agent",
|
||||||
|
"agent", cfg.Agent.ID,
|
||||||
|
"acl_empty", agentACL.Empty(),
|
||||||
|
)
|
||||||
|
|
||||||
|
a, err := agents.New(cfg, rules, agentACL, agentLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to create agent", "id", cfg.Agent.ID, "err", err)
|
logger.Error("failed to create agent", "id", cfg.Agent.ID, "err", err)
|
||||||
agentCleanup()
|
agentCleanup()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/enmanuel/agents/internal/config"
|
"github.com/enmanuel/agents/internal/config"
|
||||||
"github.com/enmanuel/agents/pkg/decision"
|
"github.com/enmanuel/agents/pkg/decision"
|
||||||
"github.com/enmanuel/agents/pkg/orchestration"
|
"github.com/enmanuel/agents/pkg/orchestration"
|
||||||
|
pksecurity "github.com/enmanuel/agents/pkg/security"
|
||||||
"github.com/enmanuel/agents/shell/bus"
|
"github.com/enmanuel/agents/shell/bus"
|
||||||
agentlog "github.com/enmanuel/agents/shell/logger"
|
agentlog "github.com/enmanuel/agents/shell/logger"
|
||||||
)
|
)
|
||||||
@@ -27,11 +28,12 @@ type runningAgent struct {
|
|||||||
|
|
||||||
// launchDeps holds shared resources needed to start/reload agents.
|
// launchDeps holds shared resources needed to start/reload agents.
|
||||||
type launchDeps struct {
|
type launchDeps struct {
|
||||||
agentBus *bus.Bus
|
agentBus *bus.Bus
|
||||||
orch *orchHandle
|
orch *orchHandle
|
||||||
logDir string
|
logDir string
|
||||||
logLevel slog.Level
|
logLevel slog.Level
|
||||||
parentCtx context.Context
|
parentCtx context.Context
|
||||||
|
secPolicy pksecurity.SecurityPolicy // centralized security policy loaded from security/
|
||||||
}
|
}
|
||||||
|
|
||||||
// agentRegistry tracks all running agents by ID, enabling individual hot-reload.
|
// agentRegistry tracks all running agents by ID, enabling individual hot-reload.
|
||||||
@@ -133,7 +135,9 @@ func (r *agentRegistry) reload(id string, rulesFor func(string, *slog.Logger) []
|
|||||||
|
|
||||||
// 5. Create new agent (validates config before discarding the old one).
|
// 5. Create new agent (validates config before discarding the old one).
|
||||||
rules := rulesFor(cfg.Agent.ID, newLogger)
|
rules := rulesFor(cfg.Agent.ID, newLogger)
|
||||||
newAgent, err := agents.New(cfg, rules, newLogger)
|
agentACL := pksecurity.ResolveACL(cfg.Agent.ID, r.deps.secPolicy)
|
||||||
|
newLogger.Debug("resolved acl for agent (reload)", "agent", cfg.Agent.ID, "acl_empty", agentACL.Empty())
|
||||||
|
newAgent, err := agents.New(cfg, rules, agentACL, newLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newLogger.Error("reload: failed to create agent", "id", id, "err", err)
|
newLogger.Error("reload: failed to create agent", "id", id, "err", err)
|
||||||
newCleanup()
|
newCleanup()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"added": "2026-03-07"
|
"added": "2026-03-07"
|
||||||
},
|
},
|
||||||
"centralized-security-groups": {
|
"centralized-security-groups": {
|
||||||
"enabled": false,
|
"enabled": true,
|
||||||
"issue": "0024",
|
"issue": "0024",
|
||||||
"description": "Sistema centralizado de grupos de usuarios y agentes para control de acceso; elimina security.roles y allowed_users por agente",
|
"description": "Sistema centralizado de grupos de usuarios y agentes para control de acceso; elimina security.roles y allowed_users por agente",
|
||||||
"added": "2026-03-08"
|
"added": "2026-03-08"
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ afectados y notas de implementacion.
|
|||||||
| 22b | E2E: Auth fixtures y helpers | [0022b-e2e-auth-helpers.md](completed/0022b-e2e-auth-helpers.md) | completado |
|
| 22b | E2E: Auth fixtures y helpers | [0022b-e2e-auth-helpers.md](completed/0022b-e2e-auth-helpers.md) | completado |
|
||||||
| 22c | E2E: Tests de agentes + docs | [0022c-e2e-agent-tests.md](completed/0022c-e2e-agent-tests.md) | completado |
|
| 22c | E2E: Tests de agentes + docs | [0022c-e2e-agent-tests.md](completed/0022c-e2e-agent-tests.md) | completado |
|
||||||
| 23 | Seccion de tests en dashboard | [0023-dashboard-tests.md](completed/0023-dashboard-tests.md) | completado |
|
| 23 | Seccion de tests en dashboard | [0023-dashboard-tests.md](completed/0023-dashboard-tests.md) | completado |
|
||||||
| 24 | Grupos y permisos centralizados | [0024-centralized-security-groups.md](0024-centralized-security-groups.md) | pendiente |
|
| 24 | Grupos y permisos centralizados | [0024-centralized-security-groups.md](completed/0024-centralized-security-groups.md) | completado |
|
||||||
| 24a | Security types: pkg/security/ | [0024a-security-types.md](completed/0024a-security-types.md) | completado |
|
| 24a | Security types: pkg/security/ | [0024a-security-types.md](completed/0024a-security-types.md) | completado |
|
||||||
| 24b | Security loader: shell/security/ | [0024b-security-loader.md](completed/0024b-security-loader.md) | completado |
|
| 24b | Security loader: shell/security/ | [0024b-security-loader.md](completed/0024b-security-loader.md) | completado |
|
||||||
| 24c | Security integration + cleanup | [0024c-security-integration.md](0024c-security-integration.md) | pendiente |
|
| 24c | Security integration + cleanup | [0024c-security-integration.md](completed/0024c-security-integration.md) | completado |
|
||||||
| 25 | Catálogo cron + scaffolder | [0025-cron-scaffolder.md](completed/0025-cron-scaffolder.md) | completado |
|
| 25 | Catálogo cron + scaffolder | [0025-cron-scaffolder.md](completed/0025-cron-scaffolder.md) | completado |
|
||||||
|
|||||||
@@ -137,6 +137,85 @@ Esto evita que el subproceso `claude -p` tenga acceso de lectura/escritura al co
|
|||||||
|
|
||||||
Implementado en `shell/llm/claudecode.go` → `resolveWorkDir()`.
|
Implementado en `shell/llm/claudecode.go` → `resolveWorkDir()`.
|
||||||
|
|
||||||
|
## 7. Sistema de grupos centralizados (`security/`)
|
||||||
|
|
||||||
|
Control de acceso centralizado: quien puede hablar con que agentes, y que puede hacer.
|
||||||
|
Reemplaza los campos per-agente `security.roles` y `matrix.filters.allowed_users` (ahora deprecados).
|
||||||
|
|
||||||
|
### Estructura de archivos
|
||||||
|
|
||||||
|
```
|
||||||
|
security/
|
||||||
|
user-groups.yaml # grupos de usuarios Matrix
|
||||||
|
agent-groups.yaml # grupos de agentes
|
||||||
|
permissions.yaml # politicas: que grupo de usuarios tiene que acciones en que agentes
|
||||||
|
```
|
||||||
|
|
||||||
|
### user-groups.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
groups:
|
||||||
|
admins:
|
||||||
|
members: ["@admin:matrix-af2f3d.organic-machine.com"]
|
||||||
|
developers:
|
||||||
|
members: ["@dev1:homeserver.com", "@dev2:homeserver.com"]
|
||||||
|
everyone:
|
||||||
|
members: ["*"] # wildcard: todos los usuarios
|
||||||
|
```
|
||||||
|
|
||||||
|
### agent-groups.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
groups:
|
||||||
|
all:
|
||||||
|
agents: ["*"] # wildcard: todos los agentes
|
||||||
|
production:
|
||||||
|
agents: ["assistant-bot", "asistente-2"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### permissions.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
policies:
|
||||||
|
- agent_group: all # aplica a todos los agentes
|
||||||
|
permissions:
|
||||||
|
- user_group: admins
|
||||||
|
actions: ["*"] # admins pueden hacer todo
|
||||||
|
- user_group: everyone
|
||||||
|
actions: ["ask"] # todos pueden chatear
|
||||||
|
- agent_group: production # solo agentes de produccion
|
||||||
|
permissions:
|
||||||
|
- user_group: developers
|
||||||
|
actions: ["ask", "command:deploy", "tool:ssh_command"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acciones disponibles
|
||||||
|
|
||||||
|
| Accion | Descripcion |
|
||||||
|
|--------|-------------|
|
||||||
|
| `*` | Todo permitido |
|
||||||
|
| `ask` | Puede chatear con el agente (mensajes normales) |
|
||||||
|
| `command:<name>` | Puede ejecutar el comando `!<name>` |
|
||||||
|
| `tool:<name>` | El LLM puede llamar la tool `<name>` para este usuario |
|
||||||
|
|
||||||
|
### Resolucion de ACL
|
||||||
|
|
||||||
|
Al arrancar, el launcher:
|
||||||
|
1. Carga todos los YAMLs con `shellsecurity.Load("security/")`
|
||||||
|
2. Para cada agente, llama `pksecurity.ResolveACL(agentID, policy)`
|
||||||
|
3. Inyecta la `acl.ACL` resultante en `agents.New()`
|
||||||
|
|
||||||
|
**Comportamiento cuando la politica esta vacia**: si `security/` no existe o no hay politicas que apliquen al agente, la ACL esta vacia y el acceso es abierto (sin restricciones). Preferible a denegar todo por defecto en produccion.
|
||||||
|
|
||||||
|
### Campos deprecados
|
||||||
|
|
||||||
|
Los siguientes campos en `config.yaml` del agente estan deprecated y no tienen efecto desde la activacion del sistema centralizado:
|
||||||
|
|
||||||
|
- `security.roles` — reemplazado por `security/permissions.yaml`
|
||||||
|
- `matrix.filters.allowed_users` — reemplazado por `security/user-groups.yaml`
|
||||||
|
|
||||||
|
Los campos siguen en el schema para compatibilidad con configs existentes y se eliminarán en un issue futuro.
|
||||||
|
|
||||||
## Activacion
|
## Activacion
|
||||||
|
|
||||||
Para activar todas las protecciones, añadir al `config.yaml` del agente:
|
Para activar todas las protecciones, añadir al `config.yaml` del agente:
|
||||||
|
|||||||
@@ -227,7 +227,8 @@ type FiltersCfg struct {
|
|||||||
DMRespond bool `yaml:"dm_respond"`
|
DMRespond bool `yaml:"dm_respond"`
|
||||||
IgnoreBots bool `yaml:"ignore_bots"`
|
IgnoreBots bool `yaml:"ignore_bots"`
|
||||||
IgnoreUsers []string `yaml:"ignore_users"`
|
IgnoreUsers []string `yaml:"ignore_users"`
|
||||||
AllowedUsers []string `yaml:"allowed_users"` // allowlist (empty = allow all)
|
// Deprecated: use security/ centralized groups instead. Kept for backward compatibility.
|
||||||
|
AllowedUsers []string `yaml:"allowed_users"`
|
||||||
UnauthorizedResponse string `yaml:"unauthorized_response"` // silent (default) | explicit
|
UnauthorizedResponse string `yaml:"unauthorized_response"` // silent (default) | explicit
|
||||||
MinPowerLevel int `yaml:"min_power_level"`
|
MinPowerLevel int `yaml:"min_power_level"`
|
||||||
}
|
}
|
||||||
@@ -287,6 +288,8 @@ type SSHTargetCfg struct {
|
|||||||
// ── Security ──────────────────────────────────────────────────────────────
|
// ── Security ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
type SecurityCfg struct {
|
type SecurityCfg struct {
|
||||||
|
// Deprecated: use security/ centralized groups instead (see security/user-groups.yaml, permissions.yaml).
|
||||||
|
// Kept for backward compatibility; will be removed in a future issue.
|
||||||
Roles map[string]RoleCfg `yaml:"roles"`
|
Roles map[string]RoleCfg `yaml:"roles"`
|
||||||
Audit AuditCfg `yaml:"audit"`
|
Audit AuditCfg `yaml:"audit"`
|
||||||
Secrets SecretsCfg `yaml:"secrets"`
|
Secrets SecretsCfg `yaml:"secrets"`
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
# Members: lista de Matrix user IDs, o "*" para todos los usuarios
|
# Members: lista de Matrix user IDs, o "*" para todos los usuarios
|
||||||
groups:
|
groups:
|
||||||
admins:
|
admins:
|
||||||
members: [] # rellenar con los administradores reales
|
members: ["@admin:matrix-af2f3d.organic-machine.com"]
|
||||||
everyone:
|
everyone:
|
||||||
members: ["*"]
|
members: ["*"]
|
||||||
|
|||||||
@@ -102,22 +102,6 @@ func (l *Listener) Run(ctx context.Context) error {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invite gating: if allowlist is configured, reject invites from unauthorized users
|
|
||||||
if len(l.cfg.Filters.AllowedUsers) > 0 {
|
|
||||||
allowed := false
|
|
||||||
for _, u := range l.cfg.Filters.AllowedUsers {
|
|
||||||
if evt.Sender.String() == u {
|
|
||||||
allowed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !allowed {
|
|
||||||
l.logger.Info("rejecting invite from unauthorized user",
|
|
||||||
"room", evt.RoomID, "inviter", evt.Sender)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.logger.Info("received room invite, joining", "room", evt.RoomID, "inviter", evt.Sender)
|
l.logger.Info("received room invite, joining", "room", evt.RoomID, "inviter", evt.Sender)
|
||||||
if _, err := l.client.raw.JoinRoom(ctx, evt.RoomID.String(), "", nil); err != nil {
|
if _, err := l.client.raw.JoinRoom(ctx, evt.RoomID.String(), "", nil); err != nil {
|
||||||
l.logger.Error("failed to auto-join room", "room", evt.RoomID, "err", err)
|
l.logger.Error("failed to auto-join room", "room", evt.RoomID, "err", err)
|
||||||
@@ -281,25 +265,6 @@ func (l *Listener) shouldHandle(evt *event.Event) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check allowlist — if configured, only allowed users can talk to the bot
|
|
||||||
if len(f.AllowedUsers) > 0 {
|
|
||||||
allowed := false
|
|
||||||
for _, u := range f.AllowedUsers {
|
|
||||||
if evt.Sender.String() == u {
|
|
||||||
allowed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !allowed {
|
|
||||||
l.logger.Debug("ignoring unauthorized user", "sender", evt.Sender)
|
|
||||||
if f.UnauthorizedResponse == "explicit" {
|
|
||||||
ctx := context.Background()
|
|
||||||
_ = l.client.SendText(ctx, evt.RoomID.String(), "No tienes permisos para interactuar con este agente.")
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if room is in the listen list
|
// Check if room is in the listen list
|
||||||
if len(l.cfg.Rooms.Listen) > 0 {
|
if len(l.cfg.Rooms.Listen) > 0 {
|
||||||
allowed := false
|
allowed := false
|
||||||
|
|||||||
Reference in New Issue
Block a user