Skip to content

Commit

Permalink
Fix #279 by moving to granular bot auth scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
moults31 committed Oct 25, 2021
1 parent 4b92ccc commit 172ddc4
Showing 7 changed files with 105 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ public class SlackCustomConnection
{
public static void main(String[] args) throws IOException
{
SlackSession session = SlackSessionFactory.getSlackSessionBuilder("my-bot-auth-token")
SlackSession session = SlackSessionFactory.getSlackSessionBuilder("my-bot-auth-token", "my-bot-app-level-token")
.withProxy(Proxy.Type.HTTP, "my.proxy.address", 1234)
.withAutoreconnectOnDisconnection(false)
.withConnectionHeartbeat(10, TimeUnit.SECONDS)
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ public class SlackDirectConnection
{
public static void main(String[] args) throws IOException
{
SlackSession session = SlackSessionFactory.createWebSocketSlackSession("my-bot-auth-token");
SlackSession session = SlackSessionFactory.createWebSocketSlackSession("my-bot-auth-token", "my-bot-app-level-token");
session.connect();
}
}
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ public class SlackDirectConnectionWithBuilder
{
public static void main(String[] args) throws IOException
{
SlackSession session = SlackSessionFactory.getSlackSessionBuilder("my-bot-auth-token").build();
SlackSession session = SlackSessionFactory.getSlackSessionBuilder("my-bot-auth-token", "my-bot-app-level-token").build();
session.connect();
}
}
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ public class SlackProxyConnection
{
public static void main(String[] args) throws IOException
{
SlackSession session = SlackSessionFactory.getSlackSessionBuilder("my-bot-auth-token")
SlackSession session = SlackSessionFactory.getSlackSessionBuilder("my-bot-auth-token", "my-bot-app-level-token")
.withProxy(Proxy.Type.HTTP, "my.proxy.address", 1234)
.build();
session.connect();
Original file line number Diff line number Diff line change
@@ -29,9 +29,9 @@
public class ChannelHistoryModuleImpl implements ChannelHistoryModule {

private final SlackSession session;
private static final String FETCH_CHANNEL_HISTORY_COMMAND = "channels.history";
private static final String FETCH_GROUP_HISTORY_COMMAND = "groups.history";
private static final String FETCH_IM_HISTORY_COMMAND = "im.history";
private static final String FETCH_CHANNEL_HISTORY_COMMAND = "conversations.history";
private static final String FETCH_GROUP_HISTORY_COMMAND = "conversations.history";
private static final String FETCH_IM_HISTORY_COMMAND = "conversations.history";
private static final int DEFAULT_HISTORY_FETCH_SIZE = 1000;

public ChannelHistoryModuleImpl(SlackSession session) {
Original file line number Diff line number Diff line change
@@ -7,18 +7,19 @@
import com.ullink.slack.simpleslackapi.WebSocketContainerProvider;

public class SlackSessionFactory {
public static SlackSession createWebSocketSlackSession(String authToken)
public static SlackSession createWebSocketSlackSession(String authToken, String appLevelToken)
{
return new SlackWebSocketSessionImpl(null, authToken, null, true, true, 0, null);
return new SlackWebSocketSessionImpl(null, authToken, appLevelToken, null, true, true, 0, null);
}

public static SlackSessionFactoryBuilder getSlackSessionBuilder(String authToken) {
return new SlackSessionFactoryBuilder(authToken);
public static SlackSessionFactoryBuilder getSlackSessionBuilder(String authToken, String appLevelToken) {
return new SlackSessionFactoryBuilder(authToken, appLevelToken);
}

public static class SlackSessionFactoryBuilder {

private String authToken;
private String appLevelToken;
private String slackBaseApi;
private Proxy.Type proxyType;
private String proxyAddress;
@@ -31,8 +32,9 @@ public static class SlackSessionFactoryBuilder {
private boolean autoreconnection;
private boolean rateLimitSupport = true;

private SlackSessionFactoryBuilder(String authToken) {
private SlackSessionFactoryBuilder(String authToken, String appLevelToken) {
this.authToken = authToken;
this.appLevelToken = appLevelToken;
}

public SlackSessionFactoryBuilder withBaseApiUrl(String slackBaseApi) {
@@ -78,7 +80,7 @@ public SlackSessionFactoryBuilder withRateLimitSupport(boolean rateLimitSupport)
}

public SlackSession build() {
return new SlackWebSocketSessionImpl(provider, authToken, slackBaseApi, proxyType, proxyAddress, proxyPort, proxyUser, proxyPassword, autoreconnection, rateLimitSupport, heartbeat, unit);
return new SlackWebSocketSessionImpl(provider, authToken, appLevelToken, slackBaseApi, proxyType, proxyAddress, proxyPort, proxyUser, proxyPassword, autoreconnection, rateLimitSupport, heartbeat, unit);
}
}
}
Original file line number Diff line number Diff line change
@@ -55,6 +55,7 @@
import com.ullink.slack.simpleslackapi.SlackPreparedMessage;
import com.ullink.slack.simpleslackapi.SlackPresence;
import com.ullink.slack.simpleslackapi.SlackSession;
import com.ullink.slack.simpleslackapi.SlackTeam;
import com.ullink.slack.simpleslackapi.SlackUser;
import com.ullink.slack.simpleslackapi.WebSocketContainerProvider;
import com.ullink.slack.simpleslackapi.blocks.Block;
@@ -100,6 +101,11 @@
import com.ullink.slack.simpleslackapi.replies.SlackReplyImpl;
import com.ullink.slack.simpleslackapi.replies.SlackUserPresenceReply;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

class SlackWebSocketSessionImpl extends AbstractSlackSessionImpl implements SlackSession, MessageHandler.Whole<String> {
private static final String DEFAULT_SLACK_API_SCHEME = "https";

@@ -118,6 +124,7 @@ private interface CONVERSATION
String INVITE_COMMAND = "conversations.invite";
String ARCHIVE_COMMAND = "conversations.archive";
String UNARCHIVE_COMMAND = "conversations.unarchive";
String LIST_COMMAND = "conversations.list";
}

private interface CHAT
@@ -133,6 +140,7 @@ private interface USERS
String SET_PRESENCE_COMMAND = "users.setPresence";
String GET_PRESENCE_COMMAND = "users.getPresence";
String LIST_COMMAND = "users.list";
String INFO_COMMAND = "users.info";
}

private interface REACTIONS
@@ -148,13 +156,20 @@ private interface REACTIONS
private static final String INVITE_USER_COMMAND = "users.admin.invite";

private static final String LIST_EMOJI_COMMAND = "emoji.list";

private static final String AUTH_TEST_COMMAND = "auth.test";

private static final String APPS_CONNECTIONS_OPEN_COMMAND = "apps.connections.open";

private static final String APP_LEVEL_API_PREFIX = "apps";

private static final Logger LOGGER = LoggerFactory.getLogger(SlackWebSocketSessionImpl.class);

private static final int DEFAULT_HEARTBEAT_IN_MILLIS = 30000;

private volatile Session websocketSession;
private final String authToken;
private final String appLevelToken;
private String slackApiBase = DEFAULT_SLACK_API_HTTPS_ROOT;
private String proxyAddress;
private int proxyPort = -1;
@@ -308,12 +323,13 @@ private <E extends SlackEvent, L extends SlackEventListener<E>> void dispatchImp
}
}

SlackWebSocketSessionImpl(WebSocketContainerProvider webSocketContainerProvider, String authToken, String slackApiBase, boolean reconnectOnDisconnection, boolean isRateLimitSupported, long heartbeat, TimeUnit unit) {
this(webSocketContainerProvider, authToken, slackApiBase, null, null, -1, null, null, reconnectOnDisconnection, isRateLimitSupported, heartbeat, unit);
SlackWebSocketSessionImpl(WebSocketContainerProvider webSocketContainerProvider, String authToken, String appLevelToken, String slackApiBase, boolean reconnectOnDisconnection, boolean isRateLimitSupported, long heartbeat, TimeUnit unit) {
this(webSocketContainerProvider, authToken, appLevelToken, slackApiBase, null, null, -1, null, null, reconnectOnDisconnection, isRateLimitSupported, heartbeat, unit);
}

SlackWebSocketSessionImpl(WebSocketContainerProvider webSocketContainerProvider, String authToken, String slackApiBase, Proxy.Type proxyType, String proxyAddress, int proxyPort, String proxyUser, String proxyPassword, boolean reconnectOnDisconnection, boolean isRateLimitSupported, long heartbeat, TimeUnit unit) {
SlackWebSocketSessionImpl(WebSocketContainerProvider webSocketContainerProvider, String authToken, String appLevelToken, String slackApiBase, Proxy.Type proxyType, String proxyAddress, int proxyPort, String proxyUser, String proxyPassword, boolean reconnectOnDisconnection, boolean isRateLimitSupported, long heartbeat, TimeUnit unit) {
this.authToken = authToken;
this.appLevelToken = appLevelToken;
if (slackApiBase != null) {
this.slackApiBase = slackApiBase;
}
@@ -381,32 +397,50 @@ public boolean isConnected()
private void connectImpl() throws IOException
{
LOGGER.info("connecting to slack");
HttpClient httpClient = getHttpClient();
HttpPost request = new HttpPost(slackApiBase + "rtm.start");
request.setHeader(HttpHeaders.CONTENT_TYPE,"application/x-www-form-urlencoded");
request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + authToken);
HttpResponse response = httpClient.execute(request);
LOGGER.debug(response.getStatusLine().toString());
String jsonResponse = consumeToString(response.getEntity().getContent());
SlackJSONSessionStatusParser sessionParser = new SlackJSONSessionStatusParser(jsonResponse);
sessionParser.parse();
if (sessionParser.getError() != null)

// Populate users
refetchUsers();

// Populate channels
String channelsAnswer = listChannels().getReply().getPlainAnswer();
JsonParser parser = new JsonParser();

JsonObject answerJson = parser.parse(channelsAnswer).getAsJsonObject();
JsonArray channelsJson = answerJson.get("channels").getAsJsonArray();

for (JsonElement jsonObject : channelsJson)
{
LOGGER.error("Error during authentication : " + sessionParser.getError());
throw new ConnectException(sessionParser.getError());
}
JsonObject jsonChannel = jsonObject.getAsJsonObject();
SlackChannel channel = SlackJSONParsingUtils.buildSlackChannel(jsonChannel, users);
LOGGER.debug("slack public channel found : " + channel.getId());
channels.put(channel.getId(), channel);
}

// Get auth test metadata
String authTestAnswer = authTest().getReply().getPlainAnswer();
JsonObject authTestJson = parser.parse(authTestAnswer).getAsJsonObject();
String user_id = authTestJson.get("user_id").getAsString();
String team_name = authTestJson.get("team").getAsString();
String team_id = authTestJson.get("team_id").getAsString();
String domain = authTestJson.get("url").getAsString();
team = new SlackTeam(team_id, team_name, domain);

// Get user info about self and build sessionPersona
String userInfoAnswer = getUserInfo(user_id).getReply().getPlainAnswer();
JsonObject userInfoJson = parser.parse(userInfoAnswer).getAsJsonObject();
sessionPersona = SlackJSONParsingUtils.buildSlackUser(userInfoJson);

// Get web socket URL
String appsConnectionsOpenAnswer = appsConnectionsOpen().getReply().getPlainAnswer();
JsonObject appsConnectionsOpenJson = parser.parse(appsConnectionsOpenAnswer).getAsJsonObject();
webSocketConnectionURL = appsConnectionsOpenJson.get("url").getAsString();

users = sessionParser.getUsers();
integrations = sessionParser.getIntegrations();
channels = sessionParser.getChannels();
sessionPersona = sessionParser.getSessionPersona();
team = sessionParser.getTeam();
LOGGER.info("Team " + team.getId() + " : " + team.getName());
LOGGER.info("Self " + sessionPersona.getId() + " : " + sessionPersona.getUserName());
LOGGER.info(users.size() + " users found on this session");
LOGGER.info(channels.size() + " channels found on this session");
webSocketConnectionURL = sessionParser.getWebSocketURL();
LOGGER.debug("retrieved websocket URL : " + webSocketConnectionURL);

establishWebsocketConnection();
}

@@ -1033,6 +1067,32 @@ public SlackMessageHandle<EmojiSlackReply> listEmoji() {
return handle;
}

public SlackMessageHandle<GenericSlackReply> listChannels()
{
Map<String, String> params = new HashMap<>();
SlackMessageHandle<GenericSlackReply> handle = postGenericSlackCommand(params, CONVERSATION.LIST_COMMAND);
return handle;
}

public SlackMessageHandle<GenericSlackReply> authTest() {
Map<String, String> params = new HashMap<>();
SlackMessageHandle<GenericSlackReply> handle = postGenericSlackCommand(params, AUTH_TEST_COMMAND);
return handle;
}

public SlackMessageHandle<GenericSlackReply> appsConnectionsOpen() {
Map<String, String> params = new HashMap<>();
SlackMessageHandle<GenericSlackReply> handle = postGenericSlackCommand(params, APPS_CONNECTIONS_OPEN_COMMAND);
return handle;
}

public SlackMessageHandle<GenericSlackReply> getUserInfo(String user_id) {
Map<String, String> params = new HashMap<>();
params.put("user", user_id);
SlackMessageHandle<GenericSlackReply> handle = postGenericSlackCommand(params, USERS.INFO_COMMAND);
return handle;
}

@Override
public void refetchUsers() {
Map<String, String> params = new HashMap<>();
@@ -1104,14 +1164,21 @@ private <T extends SlackReply> void postSlackCommandWithFile(Map<String, String>
public SlackMessageHandle<GenericSlackReply> postGenericSlackCommand(Map<String, String> params, String command) {
HttpClient client = getHttpClient();
HttpPost request = new HttpPost(slackApiBase + command);
request.setHeader(HttpHeaders.CONTENT_TYPE,"application/x-www-form-urlencoded");
List<NameValuePair> nameValuePairList = new ArrayList<>();
for (Map.Entry<String, String> arg : params.entrySet())
{
if (!"token".equals(arg.getKey())) {
nameValuePairList.add(new BasicNameValuePair(arg.getKey(), arg.getValue()));
}
}
nameValuePairList.add(new BasicNameValuePair("token", authToken));
if (command.contains(APP_LEVEL_API_PREFIX)) {
request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + appLevelToken);
}
else {
request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + authToken);
}

try
{
SlackMessageHandle<GenericSlackReply> handle = new SlackMessageHandle<>(getNextMessageId());

0 comments on commit 172ddc4

Please sign in to comment.