Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #279 by moving to granular bot auth scopes #3

Merged
merged 1 commit into from
Oct 25, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Up @@ -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;
Expand Down Expand Up @@ -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";

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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<>();
Expand Down Expand Up @@ -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());
Expand Down