diff --git a/golemcore/slack/plugin.yaml b/golemcore/slack/plugin.yaml index 8797145..678865c 100644 --- a/golemcore/slack/plugin.yaml +++ b/golemcore/slack/plugin.yaml @@ -1,7 +1,7 @@ id: golemcore/slack provider: golemcore name: slack -version: 1.0.3 +version: 1.0.4 pluginApiVersion: 1 engineVersion: ">=0.0.0 <1.0.0" entrypoint: me.golemcore.plugins.golemcore.slack.SlackPluginBootstrap diff --git a/golemcore/slack/pom.xml b/golemcore/slack/pom.xml index ab9f7f2..69b20ec 100644 --- a/golemcore/slack/pom.xml +++ b/golemcore/slack/pom.xml @@ -9,7 +9,7 @@ ../../pom.xml - 1.0.3 + 1.0.4 golemcore-slack-plugin golemcore/slack Slack Socket Mode channel plugin with interactive approvals for GolemCore diff --git a/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfig.java b/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfig.java index 6913d20..8d2c0de 100644 --- a/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfig.java +++ b/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfig.java @@ -7,11 +7,6 @@ import lombok.Data; import lombok.NoArgsConstructor; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - @JsonIgnoreProperties(ignoreUnknown = true) @Data @NoArgsConstructor @@ -28,12 +23,6 @@ public class SlackPluginConfig { @Builder.Default private Boolean replyInThread = true; - @Builder.Default - private List allowedUserIds = new ArrayList<>(); - - @Builder.Default - private List allowedChannelIds = new ArrayList<>(); - public void normalize() { if (enabled == null) { enabled = false; @@ -43,8 +32,6 @@ public void normalize() { } botToken = normalizeSecret(botToken); appToken = normalizeSecret(appToken); - allowedUserIds = normalizeIdentifiers(allowedUserIds); - allowedChannelIds = normalizeIdentifiers(allowedChannelIds); } @JsonIgnore @@ -60,21 +47,4 @@ private String normalizeSecret(String value) { String normalized = value.trim(); return normalized.isBlank() ? null : normalized; } - - private List normalizeIdentifiers(List values) { - if (values == null || values.isEmpty()) { - return List.of(); - } - Set normalized = new LinkedHashSet<>(); - for (String value : values) { - if (value == null) { - continue; - } - String candidate = value.trim(); - if (!candidate.isBlank()) { - normalized.add(candidate); - } - } - return List.copyOf(normalized); - } } diff --git a/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/SlackPluginSettingsContributor.java b/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/SlackPluginSettingsContributor.java index 76ab8c9..84935fe 100644 --- a/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/SlackPluginSettingsContributor.java +++ b/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/SlackPluginSettingsContributor.java @@ -56,8 +56,6 @@ public PluginSettingsSection getSection(String sectionKey) { values.put("botToken", ""); values.put("appToken", ""); values.put("replyInThread", Boolean.TRUE.equals(config.getReplyInThread())); - values.put("allowedUserIds", String.join(", ", config.getAllowedUserIds())); - values.put("allowedChannelIds", String.join(", ", config.getAllowedChannelIds())); return PluginSettingsSection.builder() .title("Slack") @@ -92,21 +90,6 @@ public PluginSettingsSection getSection(String sectionKey) { .label("Reply In Threads") .description( "Route channel mentions into one session per root thread and answer in that thread.") - .build(), - PluginSettingsField.builder() - .key("allowedUserIds") - .type("text") - .label("Allowed User IDs") - .description("Optional comma-separated Slack user ids. Leave blank to allow any user.") - .placeholder("U01234567, U08999999") - .build(), - PluginSettingsField.builder() - .key("allowedChannelIds") - .type("text") - .label("Allowed Channel IDs") - .description( - "Optional comma-separated Slack channel ids. Leave blank to allow any channel or DM.") - .placeholder("C01234567, D08999999") .build())) .values(values) .blocks(List.of(PluginSettingsBlock.builder() @@ -141,9 +124,6 @@ public PluginSettingsSection saveSection(String sectionKey, Map config.setAppToken(appToken); } - config.setAllowedUserIds(parseCsv(readString(values, "allowedUserIds", ""))); - config.setAllowedChannelIds(parseCsv(readString(values, "allowedChannelIds", ""))); - configService.save(config); eventPublisher.publishEvent(new SlackRestartEvent()); return getSection(sectionKey); @@ -177,15 +157,4 @@ private String readString(Map values, String key, String default Object value = values.get(key); return value instanceof String text ? text : defaultValue; } - - private List parseCsv(String raw) { - if (raw == null || raw.isBlank()) { - return List.of(); - } - return java.util.Arrays.stream(raw.split(",")) - .map(String::trim) - .filter(value -> !value.isBlank()) - .distinct() - .toList(); - } } diff --git a/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/adapter/inbound/slack/SlackAdapter.java b/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/adapter/inbound/slack/SlackAdapter.java index bc22061..1e508f0 100644 --- a/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/adapter/inbound/slack/SlackAdapter.java +++ b/golemcore/slack/src/main/java/me/golemcore/plugins/golemcore/slack/adapter/inbound/slack/SlackAdapter.java @@ -180,8 +180,7 @@ public CompletableFuture sendProgressUpdate(String chatId, ProgressUpdate @Override public boolean isAuthorized(String senderId) { - SlackPluginConfig config = configService.getConfig(); - return config.getAllowedUserIds().isEmpty() || config.getAllowedUserIds().contains(senderId); + return true; } @Override @@ -214,14 +213,6 @@ private void handleInbound(SlackInboundEnvelope envelope) { if (envelope == null) { return; } - if (!isAuthorized(envelope.userId())) { - log.warn("[Slack] Ignoring message from unauthorized user {}", envelope.userId()); - return; - } - if (!isChannelAllowed(envelope.channelId())) { - log.warn("[Slack] Ignoring message from unauthorized channel {}", envelope.channelId()); - return; - } if (requiresExistingConversation(envelope) && !hasExistingConversation(envelope)) { log.debug("[Slack] Ignoring thread reply without active conversation {}", envelope.rootThreadTs()); return; @@ -239,15 +230,6 @@ private void handleAction(SlackActionEnvelope envelope) { if (envelope == null) { return; } - if (!isAuthorized(envelope.userId())) { - log.warn("[Slack] Ignoring action from unauthorized user {}", envelope.userId()); - return; - } - if (!isChannelAllowed(envelope.channelId())) { - log.warn("[Slack] Ignoring action from unauthorized channel {}", envelope.channelId()); - return; - } - String actionId = envelope.actionId(); if (SlackActionIds.CONFIRM_APPROVE.equals(actionId) || SlackActionIds.CONFIRM_CANCEL.equals(actionId)) { eventPublisher.publishEvent(new ConfirmationCallbackEvent( @@ -266,11 +248,6 @@ private void handleAction(SlackActionEnvelope envelope) { } } - private boolean isChannelAllowed(String channelId) { - SlackPluginConfig config = configService.getConfig(); - return config.getAllowedChannelIds().isEmpty() || config.getAllowedChannelIds().contains(channelId); - } - private Message buildInboundMessage(SlackInboundEnvelope envelope) { SlackConversationTarget target = resolveConversationTarget(envelope); diff --git a/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfigServiceTest.java b/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfigServiceTest.java index 989da9d..990b9f0 100644 --- a/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfigServiceTest.java +++ b/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfigServiceTest.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import com.fasterxml.jackson.databind.ObjectMapper; + import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -18,6 +20,7 @@ class SlackPluginConfigServiceTest { private PluginConfigurationService pluginConfigurationService; private SlackPluginConfigService service; + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach void setUp() { @@ -37,12 +40,13 @@ void shouldIgnoreLegacyConfiguredFieldWhenReadingStoredPluginConfig() { "allowedChannelIds", java.util.List.of("C123"))); SlackPluginConfig config = service.getConfig(); + Map serialized = objectMapper.convertValue(config, Map.class); assertEquals("xoxb-test", config.getBotToken()); assertEquals("xapp-test", config.getAppToken()); assertFalse(Boolean.TRUE.equals(config.getReplyInThread())); - assertEquals(java.util.List.of("U123"), config.getAllowedUserIds()); - assertEquals(java.util.List.of("C123"), config.getAllowedChannelIds()); + assertFalse(serialized.containsKey("allowedUserIds")); + assertFalse(serialized.containsKey("allowedChannelIds")); } @Test @@ -52,8 +56,6 @@ void shouldPersistNormalizedPluginConfigWithoutComputedFields() { .botToken(" xoxb-token ") .appToken(" xapp-token ") .replyInThread(null) - .allowedUserIds(java.util.List.of(" U123 ", "", "U123")) - .allowedChannelIds(java.util.List.of(" C123 ", "C123")) .build(); service.save(config); @@ -66,8 +68,8 @@ void shouldPersistNormalizedPluginConfigWithoutComputedFields() { assertEquals("xapp-token", saved.get("appToken")); assertEquals(true, saved.get("enabled")); assertEquals(true, saved.get("replyInThread")); - assertEquals(java.util.List.of("U123"), saved.get("allowedUserIds")); - assertEquals(java.util.List.of("C123"), saved.get("allowedChannelIds")); + assertFalse(saved.containsKey("allowedUserIds")); + assertFalse(saved.containsKey("allowedChannelIds")); assertFalse(saved.containsKey("configured")); } } diff --git a/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfigTest.java b/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfigTest.java index 2c9ca2d..ec0fc89 100644 --- a/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfigTest.java +++ b/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginConfigTest.java @@ -2,8 +2,6 @@ import org.junit.jupiter.api.Test; -import java.util.List; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -11,14 +9,12 @@ class SlackPluginConfigTest { @Test - void shouldNormalizeDefaultsAndIdentifiers() { + void shouldNormalizeDefaultsAndSecrets() { SlackPluginConfig config = SlackPluginConfig.builder() .enabled(null) .replyInThread(null) .botToken(" ") .appToken(" xapp-token ") - .allowedUserIds(List.of(" U123 ", "", "U123", "U999")) - .allowedChannelIds(List.of(" C123 ", "C123", "D456")) .build(); config.normalize(); @@ -27,7 +23,5 @@ void shouldNormalizeDefaultsAndIdentifiers() { assertTrue(Boolean.TRUE.equals(config.getReplyInThread())); assertNull(config.getBotToken()); assertEquals("xapp-token", config.getAppToken()); - assertEquals(List.of("U123", "U999"), config.getAllowedUserIds()); - assertEquals(List.of("C123", "D456"), config.getAllowedChannelIds()); } } diff --git a/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginSettingsContributorTest.java b/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginSettingsContributorTest.java index 9ba26fc..caeed53 100644 --- a/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginSettingsContributorTest.java +++ b/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/SlackPluginSettingsContributorTest.java @@ -1,5 +1,6 @@ package me.golemcore.plugins.golemcore.slack; +import com.fasterxml.jackson.databind.ObjectMapper; import me.golemcore.plugin.api.extension.spi.PluginActionResult; import me.golemcore.plugin.api.extension.spi.PluginSettingsSection; import org.junit.jupiter.api.BeforeEach; @@ -7,7 +8,6 @@ import org.mockito.ArgumentCaptor; import org.springframework.context.ApplicationEventPublisher; -import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -22,6 +22,7 @@ class SlackPluginSettingsContributorTest { private ApplicationEventPublisher eventPublisher; private SlackPluginSettingsContributor contributor; private SlackPluginConfig config; + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach void setUp() { @@ -33,8 +34,6 @@ void setUp() { .botToken("xoxb-existing") .appToken("xapp-existing") .replyInThread(true) - .allowedUserIds(List.of("U123")) - .allowedChannelIds(List.of("C123")) .build(); when(configService.getConfig()).thenReturn(config); } @@ -45,8 +44,8 @@ void shouldExposeSectionWithBlankSecrets() { assertEquals("", section.getValues().get("botToken")); assertEquals("", section.getValues().get("appToken")); - assertEquals("U123", section.getValues().get("allowedUserIds")); - assertEquals("C123", section.getValues().get("allowedChannelIds")); + assertTrue(!section.getValues().containsKey("allowedUserIds")); + assertTrue(!section.getValues().containsKey("allowedChannelIds")); } @Test @@ -55,19 +54,18 @@ void shouldPreserveSecretsWhenBlankValuesAreSaved() { "enabled", false, "botToken", "", "appToken", "", - "replyInThread", false, - "allowedUserIds", "U777, U888", - "allowedChannelIds", "D123, C999")); + "replyInThread", false)); ArgumentCaptor captor = ArgumentCaptor.forClass(SlackPluginConfig.class); verify(configService).save(captor.capture()); SlackPluginConfig saved = captor.getValue(); + Map serialized = objectMapper.convertValue(saved, Map.class); assertEquals("xoxb-existing", saved.getBotToken()); assertEquals("xapp-existing", saved.getAppToken()); assertTrue(Boolean.FALSE.equals(saved.getEnabled())); assertTrue(Boolean.FALSE.equals(saved.getReplyInThread())); - assertEquals(List.of("U777", "U888"), saved.getAllowedUserIds()); - assertEquals(List.of("D123", "C999"), saved.getAllowedChannelIds()); + assertTrue(!serialized.containsKey("allowedUserIds")); + assertTrue(!serialized.containsKey("allowedChannelIds")); verify(eventPublisher).publishEvent(org.mockito.ArgumentMatchers.any(SlackRestartEvent.class)); } diff --git a/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/adapter/inbound/slack/SlackAdapterTest.java b/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/adapter/inbound/slack/SlackAdapterTest.java index d2ecc41..ac7d51e 100644 --- a/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/adapter/inbound/slack/SlackAdapterTest.java +++ b/golemcore/slack/src/test/java/me/golemcore/plugins/golemcore/slack/adapter/inbound/slack/SlackAdapterTest.java @@ -1,5 +1,6 @@ package me.golemcore.plugins.golemcore.slack.adapter.inbound.slack; +import com.fasterxml.jackson.databind.ObjectMapper; import me.golemcore.plugin.api.extension.loop.AgentLoop; import me.golemcore.plugin.api.extension.model.ConfirmationCallbackEvent; import me.golemcore.plugin.api.extension.model.ContextAttributes; @@ -48,6 +49,7 @@ class SlackAdapterTest { private ApplicationEventPublisher eventPublisher; private SlackAdapter adapter; private SlackPluginConfig config; + private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach void setUp() { @@ -103,7 +105,13 @@ void shouldPublishInboundThreadedMessage() { @Test void shouldIgnoreUnauthorizedUsers() { - config.setAllowedUserIds(List.of("U111")); + config = objectMapper.convertValue(Map.of( + "enabled", true, + "botToken", "xoxb-test", + "appToken", "xapp-test", + "replyInThread", true, + "allowedUserIds", List.of("U111")), SlackPluginConfig.class); + when(configService.getConfig()).thenReturn(config); adapter.start(); ArgumentCaptor> captor = ArgumentCaptor.forClass(Consumer.class); @@ -119,7 +127,7 @@ void shouldIgnoreUnauthorizedUsers() { true, false)); - verify(eventPublisher, never()).publishEvent(any()); + verify(eventPublisher).publishEvent(any(AgentLoop.InboundMessageEvent.class)); } @Test diff --git a/registry/golemcore/slack/index.yaml b/registry/golemcore/slack/index.yaml index 0bfcc7c..7016a14 100644 --- a/registry/golemcore/slack/index.yaml +++ b/registry/golemcore/slack/index.yaml @@ -1,10 +1,11 @@ id: golemcore/slack owner: golemcore name: slack -latest: 1.0.3 +latest: 1.0.4 versions: - 1.0.0 - 1.0.1 - 1.0.2 - 1.0.3 + - 1.0.4 source: "https://github.com/alexk-dev/golemcore-plugins/tree/main/golemcore/slack" diff --git a/registry/golemcore/slack/versions/1.0.4.yaml b/registry/golemcore/slack/versions/1.0.4.yaml new file mode 100644 index 0000000..e4b59f7 --- /dev/null +++ b/registry/golemcore/slack/versions/1.0.4.yaml @@ -0,0 +1,13 @@ +id: golemcore/slack +version: 1.0.4 +pluginApiVersion: 1 +engineVersion: ">=0.0.0 <1.0.0" +artifactUrl: "dist/golemcore/slack/1.0.4/golemcore-slack-plugin-1.0.4.jar" +checksumSha256: "bd1d03fb07a9c8173a087acd64a2764fa5a3b903e7fcbbbc4468a830e84ee72f" +publishedAt: "2026-03-22T17:22:33Z" +sourceCommit: "57967fbeb9aee29b37e3cc60367c122611a84641" +entrypoint: me.golemcore.plugins.golemcore.slack.SlackPluginBootstrap +sourceUrl: "https://github.com/alexk-dev/golemcore-plugins/tree/main/golemcore/slack" +license: "Apache-2.0" +maintainers: + - alexk-dev