Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion golemcore/slack/plugin.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion golemcore/slack/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<relativePath>../../pom.xml</relativePath>
</parent>

<version>1.0.3</version>
<version>1.0.4</version>
<artifactId>golemcore-slack-plugin</artifactId>
<name>golemcore/slack</name>
<description>Slack Socket Mode channel plugin with interactive approvals for GolemCore</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,12 +23,6 @@ public class SlackPluginConfig {
@Builder.Default
private Boolean replyInThread = true;

@Builder.Default
private List<String> allowedUserIds = new ArrayList<>();

@Builder.Default
private List<String> allowedChannelIds = new ArrayList<>();

public void normalize() {
if (enabled == null) {
enabled = false;
Expand All @@ -43,8 +32,6 @@ public void normalize() {
}
botToken = normalizeSecret(botToken);
appToken = normalizeSecret(appToken);
allowedUserIds = normalizeIdentifiers(allowedUserIds);
allowedChannelIds = normalizeIdentifiers(allowedChannelIds);
}

@JsonIgnore
Expand All @@ -60,21 +47,4 @@ private String normalizeSecret(String value) {
String normalized = value.trim();
return normalized.isBlank() ? null : normalized;
}

private List<String> normalizeIdentifiers(List<String> values) {
if (values == null || values.isEmpty()) {
return List.of();
}
Set<String> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -141,9 +124,6 @@ public PluginSettingsSection saveSection(String sectionKey, Map<String, Object>
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);
Expand Down Expand Up @@ -177,15 +157,4 @@ private String readString(Map<String, Object> values, String key, String default
Object value = values.get(key);
return value instanceof String text ? text : defaultValue;
}

private List<String> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,7 @@ public CompletableFuture<Void> 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
Expand Down Expand Up @@ -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;
Expand All @@ -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(
Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,6 +20,7 @@ class SlackPluginConfigServiceTest {

private PluginConfigurationService pluginConfigurationService;
private SlackPluginConfigService service;
private final ObjectMapper objectMapper = new ObjectMapper();

@BeforeEach
void setUp() {
Expand All @@ -37,12 +40,13 @@ void shouldIgnoreLegacyConfiguredFieldWhenReadingStoredPluginConfig() {
"allowedChannelIds", java.util.List.of("C123")));

SlackPluginConfig config = service.getConfig();
Map<String, Object> 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
Expand All @@ -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);
Expand All @@ -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"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,19 @@

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;

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();
Expand All @@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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;
import org.junit.jupiter.api.Test;
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;
Expand All @@ -22,6 +22,7 @@ class SlackPluginSettingsContributorTest {
private ApplicationEventPublisher eventPublisher;
private SlackPluginSettingsContributor contributor;
private SlackPluginConfig config;
private final ObjectMapper objectMapper = new ObjectMapper();

@BeforeEach
void setUp() {
Expand All @@ -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);
}
Expand All @@ -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
Expand All @@ -55,19 +54,18 @@ void shouldPreserveSecretsWhenBlankValuesAreSaved() {
"enabled", false,
"botToken", "",
"appToken", "",
"replyInThread", false,
"allowedUserIds", "U777, U888",
"allowedChannelIds", "D123, C999"));
"replyInThread", false));

ArgumentCaptor<SlackPluginConfig> captor = ArgumentCaptor.forClass(SlackPluginConfig.class);
verify(configService).save(captor.capture());
SlackPluginConfig saved = captor.getValue();
Map<String, Object> 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));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -48,6 +49,7 @@ class SlackAdapterTest {
private ApplicationEventPublisher eventPublisher;
private SlackAdapter adapter;
private SlackPluginConfig config;
private final ObjectMapper objectMapper = new ObjectMapper();

@BeforeEach
void setUp() {
Expand Down Expand Up @@ -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<Consumer<SlackInboundEnvelope>> captor = ArgumentCaptor.forClass(Consumer.class);
Expand All @@ -119,7 +127,7 @@ void shouldIgnoreUnauthorizedUsers() {
true,
false));

verify(eventPublisher, never()).publishEvent(any());
verify(eventPublisher).publishEvent(any(AgentLoop.InboundMessageEvent.class));
}

@Test
Expand Down
3 changes: 2 additions & 1 deletion registry/golemcore/slack/index.yaml
Original file line number Diff line number Diff line change
@@ -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"
Loading
Loading