Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Add slack classic bot platform #170

Merged
merged 4 commits into from
Dec 1, 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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ RUN pip install \
-r requirements.txt
RUN pip install errbot[slack]

COPY data ./data
COPY config.py .
COPY errbot-slack-bolt-backend ./errbot-slack-bolt-backend
COPY errbot-backend-botframework ./errbot-backend-botframework

RUN mkdir ./data
RUN mkdir -p plugins/sdm
COPY plugins/sdm ./plugins/sdm/

Expand Down
21 changes: 16 additions & 5 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,35 @@ def get_access_controls():
}

def get_bot_identity():
if os.getenv('SDM_BOT_PLATFORM') == 'ms-teams':
platform = os.getenv('SDM_BOT_PLATFORM')
if platform == 'ms-teams':
return {
"appId": os.getenv("AZURE_APP_ID"),
"appPassword": os.getenv("AZURE_APP_PASSWORD")
}
elif platform == 'slack-classic':
return {
'token': os.getenv("SLACK_TOKEN")
}
return {
"app_token": os.environ["SLACK_APP_TOKEN"],
"bot_token": os.environ["SLACK_BOT_TOKEN"],
"app_token": os.getenv("SLACK_APP_TOKEN"),
"bot_token": os.getenv("SLACK_BOT_TOKEN"),
}

def get_backend():
if os.getenv('SDM_BOT_PLATFORM') == 'ms-teams':
platform = os.getenv('SDM_BOT_PLATFORM')
if platform == 'ms-teams':
return 'BotFramework'
elif platform == 'slack-classic':
return 'Slack'
return 'SlackBolt'

def get_bot_extra_backend_dir():
if os.getenv('SDM_BOT_PLATFORM') == 'ms-teams':
platform = os.getenv('SDM_BOT_PLATFORM')
if platform == 'ms-teams':
return 'errbot-backend-botframework'
elif platform == 'slack-classic':
return None
return 'errbot-slack-bolt-backend/errbot_slack_bolt_backend'


Expand Down
16 changes: 5 additions & 11 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@ version: "3.9"
services:
accessbot:
image: public.ecr.aws/strongdm/accessbot:latest
environment:
# IMPORTANT: Do not enclose values in double or single quotes
env_file:
# You could use env-file.example as a reference
- env-file
ports:
- 3141:3141

# Required variables
- SLACK_APP_TOKEN=slack-app-token
- SLACK_BOT_TOKEN=slack-bot-token
- SDM_ADMINS=@slack-handle1 @slack-handle2 # use space for configuring multiple slack handles
- SDM_API_ACCESS_KEY=sdm-api-acess-key
- SDM_API_SECRET_KEY=sdm-api-secret-key

# Optional variables
# See: docs/CONFIGURE_ACCESSBOT.md
16 changes: 13 additions & 3 deletions docs/CONFIGURE_ACCESSBOT.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@
There are a number of variables you can use for configuring AccessBot.

## Required configuration
* **SLACK_APP_TOKEN**. Slack App-Level Token
* **SLACK_BOT_TOKEN**. Slack Bot User OAuth Token
* **SDM_ADMINS**. List of Slack admins, format: `@usernick`. Although it's not required, these users are often SDM admins too. You could use `whoami` for getting user nicks.
* **SDM_ADMINS**. List of Slack admins, format: `@usernick`. Although it's not required, these users are often SDM admins too. For getting user nicks, you could use the command `whoami` or the [tools/get-slack-handle.py](../tools/get-slack-handle.py) script.
* **SDM_API_ACCESS_KEY**. SDM API Access Key
* **SDM_API_SECRET_KEY**. SDM API Access Key Secret

### Slack (SDM_BOT_PLATFORM='slack' / default)
* **SLACK_APP_TOKEN**. Slack App-Level Token
* **SLACK_BOT_TOKEN**. Slack Bot User OAuth Token

### Slack Classic (SDM_BOT_PLATFORM='slack-classic')
* **SLACK_TOKEN**. Slack Bot User OAuth Token for Classic Slack bot version

### MS Teams (SDM_BOT_PLATFORM='ms-teams')
* **AZURE_APP_ID**. Set to the **Microsoft App ID**
* **AZURE_APP_PASSWORD**. Set to the **Secret Value**

## Internal configuration
* **LOG_LEVEL**. Logging level. Default = INFO
* **SDM_DOCKERIZED**. Logging type. Default = true (_when using docker_), meaning logs go to STDOUT
Expand Down Expand Up @@ -48,6 +57,7 @@ See image below for more information:
`System Preferences > Keyboard > Text > Uncheck "Use smart quotes and dashes`. The `config` command fails to understand quotes as unicode characters.

### Using Tags
A snippet that might help:

#### Allow Resource
```
Expand Down
2 changes: 1 addition & 1 deletion docs/CONFIGURE_ALTERNATIVE_EMAILS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

You can make access requests using alternative emails. This functionality is specially helpful when you need to make access requests using a different email address than the one you have configured in your Slack Profile.

**_Custom profile fields are only available for Slack Business+ workspaces_**
**_Custom profile fields are only available for Slack Business+ workspaces and Bolt API_**

Follow these steps to configure in your Slack Workspace:

Expand Down
34 changes: 30 additions & 4 deletions docs/CONFIGURE_LOCAL_ENV.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,45 @@ pip install -r requirements/dev.txt

## Variables configuration
```
export SLACK_APP_TOKEN=slack-app-token
export SLACK_BOT_TOKEN=slack-bot-token
export SDM_API_ACCESS_KEY=api-access-key
export SDM_API_SECRET_KEY=api-secret-key
export SDM_ADMINS=@admin1 # if multiple, use: @admin1 @admin2
```

See [Configure Slack](CONFIGURE_SLACK.md) and [Configure SDM](CONFIGURE_SDM.md)
### BOT PLATFORM variables configuration

See the subsessions about SDM_BOT_PLATFORM specific variables:

#### SDM_BOT_PLATFORM is `slack`
```
export SLACK_APP_TOKEN=slack-app-token
export SLACK_BOT_TOKEN=slack-bot-token
```

See [Configure Slack](CONFIGURE_SLACK.md)

#### SDM_BOT_PLATFORM is `slack-classic`
```
export SLACK_TOKEN=slack-token
```

See [Configure Slack Classic Bot](CONFIGURE_SLACK_CLASSIC.md)

#### SDM_BOT_PLATFORM is `ms-teams`:
```
export AZURE_APP_ID=app-id
export AZURE_APP_PASSWORD=app-password
```

See [Configure Microsoft Teams](CONFIGURE_MS_TEAMS.md)

---

Before initialize errbot, you also need to [Configure SDM](CONFIGURE_SDM.md).

## Initialize errbot
```
mv config.py config.py.back
errbot --init
pip install errbot[slack]
mv config.py.back config.py
```
Expand Down
31 changes: 31 additions & 0 deletions docs/CONFIGURE_SLACK_CLASSIC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Configure Slack

In order to configure AccessBot integration with Slack follow the next steps:

1. Go to https://api.slack.com/apps?new_classic_app=1 and create a classic app

![image](https://user-images.githubusercontent.com/313803/115708663-936d2380-a370-11eb-94d2-b5edb1596af7.png)

2. Go to OAuth & Permissions and add bot scope in the Scopes

![image](https://user-images.githubusercontent.com/313803/115709326-653c1380-a371-11eb-9346-f2fa81c7fd24.png)

IMPORTANT: The reason why you need a classic app and the bot scope, is because the current AccessBot implementation uses the RTM API, which is not available
when updating to the new bot scopes.

4. Go to App Home

![image](https://user-images.githubusercontent.com/313803/115710249-6cafec80-a372-11eb-9071-bad38cf0d4bf.png)

5. Click Add Legacy Bot User and set its name

![image](https://user-images.githubusercontent.com/313803/115710432-a2ed6c00-a372-11eb-8fda-b8ef9c874e49.png)

6. Go to Install App

![image](https://user-images.githubusercontent.com/313803/115710557-c6181b80-a372-11eb-95dd-72927c81e53a.png)


**Use "Bot User OAuth Token" for your _SLACK_TOKEN_ variable**

_Original instructions from [this thread](https://github.com/slackapi/python-slack-sdk/issues/609#issuecomment-6398872129)_
42 changes: 42 additions & 0 deletions env-file.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# You can copy this file as "env-file" for your docker-compose
# IMPORTANT: Do not enclose values in double or single quotes

# ------------------------------------------------------------------------------
# | GENERAL ENV VARS |
# ------------------------------------------------------------------------------
# These vars are required for any SDM_BOT_PLAFORM.

SDM_BOT_PLATFORM=slack # possible values: slack, slack-classic, ms-teams
SDM_API_ACCESS_KEY=
SDM_API_SECRET_KEY=

# ------------------------------------------------------------------------------
# | SLACK BOLT ENV VARS |
# ------------------------------------------------------------------------------
# You need to use the following vars when SDM_BOT_PLATFORM var is "slack":

# SLACK_APP_TOKEN=
# SLACK_BOT_TOKEN=
# SDM_ADMINS=@nickname

# ------------------------------------------------------------------------------
# | SLACK CLASSIC ENV VARS |
# ------------------------------------------------------------------------------
# You need to use the following vars when SDM_BOT_PLATFORM var is "slack-classic":

# SLACK_TOKEN=
# SDM_ADMINS=@nickname

# ------------------------------------------------------------------------------
# | MS-TEAMS ENV VARS |
# ------------------------------------------------------------------------------
# You need to use the following vars when SDM_BOT_PLATFORM var is "ms-teams":

# [email protected]
# AZURE_APP_ID=
# AZURE_APP_PASSWORD=

# ------------------------------------------------------------------------------
# | OPTIONAL VARS |
# ------------------------------------------------------------------------------
# See: docs/CONFIGURE_ACCESSBOT.md
2 changes: 1 addition & 1 deletion ms-teams/dev/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

errbot &
pid[0]=$!
ssh -N -R 3141:localhost:3141 -i $LOG_EXPORT_CONTAINER_SSH_CREDENTIALS $LOG_EXPORT_CONTAINER_SSH_DESTINATION &
ssh -N -R 3141:localhost:3141 -i $ACCESSBOT_WEBHOOK_SSH_CREDENTIALS $ACCESSBOT_WEBHOOK_SSH_DESTINATION &
pid[1]=$!
trap "kill ${pid[0]} ${pid[1]}; exit 1" INT
wait
4 changes: 2 additions & 2 deletions plugins/sdm/accessbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ def get_sdm_email_from_profile(self, sender, email_field):
def clean_up_message(self, message):
return self._platform.clean_up_message(message)

def format_access_request_params(self, resource_name, sender_nick, request_id):
return self._platform.format_access_request_params(resource_name, sender_nick, request_id)
def format_access_request_params(self, resource_name, sender_nick):
return self._platform.format_access_request_params(resource_name, sender_nick)

def format_strikethrough(self, text):
return self._platform.format_strikethrough(text)
Expand Down
7 changes: 3 additions & 4 deletions plugins/sdm/lib/helper/base_grant_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ def __grant_access(self, message, sdm_object, sdm_account, execution_id, request
self.__bot.log.info("##SDM## %s GrantHelper.__grant_%s sender_nick: %s sender_email: %s", execution_id, self.__grant_type, sender_nick, sender_email)
self.__enter_grant_request(message, sdm_object, sdm_account, self.__grant_type, request_id)
if not self.__needs_auto_approve(sdm_object) or self.__reached_max_auto_approve_uses(message.frm.person):
# TODO: ADD EXTRAS
yield from self.__notify_access_request_entered(sender_nick, sdm_object.name, request_id, message)
self.__bot.log.debug("##SDM## %s GrantHelper.__grant_%s needs manual approval", execution_id, self.__grant_type)
return
Expand All @@ -90,9 +89,9 @@ def __reached_max_auto_approve_uses(self, requester_id):
def __notify_access_request_entered(self, sender_nick, resource_name, request_id, message):
team_admins = ", ".join(self.__bot.get_admins())
operation_desc = self.get_operation_desc()
resource_name, sender_nick, request_id = self.__bot.format_access_request_params(resource_name, sender_nick, request_id)
yield f"Thanks {sender_nick}, that is a valid request. Let me check with the team admins: {team_admins}\nYour request id is {request_id}"
self.__notify_admins(f"Hey I have an {operation_desc} request from USER {sender_nick} for {self.__grant_type.name} {resource_name}! To approve, enter: **yes** {request_id}", message)
formatted_resource_name, formatted_sender_nick = self.__bot.format_access_request_params(resource_name, sender_nick)
yield f"Thanks {formatted_sender_nick}, that is a valid request. Let me check with the team admins: {team_admins}\nYour request id is **{request_id}**"
self.__notify_admins(f"Hey I have an {operation_desc} request from USER {formatted_sender_nick} for {self.__grant_type.name} {formatted_resource_name}! To approve, enter: **yes {request_id}**", message)

def __notify_admins(self, text, message):
admins_channel = self.__bot.config['ADMINS_CHANNEL']
Expand Down
2 changes: 1 addition & 1 deletion plugins/sdm/lib/helper/poller_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def stale_grant_requests_cleaner(self):
for request_id in self.__bot.get_grant_request_ids():
grant_request = self.__bot.get_grant_request(request_id)
elapsed_time = time.time() - grant_request['timestamp']
if elapsed_time > self.__bot.config['ADMIN_TIMEOUT']:
if elapsed_time >= self.__bot.config['ADMIN_TIMEOUT']:
self.__bot.log.info("##SDM## Cleaning grant requests, stale request_id = %s", request_id)
self.__notify_grant_request_denied(grant_request)
self.__bot.remove_grant_request(request_id)
Expand Down
2 changes: 1 addition & 1 deletion plugins/sdm/lib/platform/base_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def clean_up_message(self, text):
pass

@abstractmethod
def format_access_request_params(self, resource_name, sender_nick, request_id):
def format_access_request_params(self, resource_name, sender_nick):
pass

@abstractmethod
Expand Down
7 changes: 2 additions & 5 deletions plugins/sdm/lib/platform/ms_teams_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,8 @@ def get_user_nick(self, approver):
def clean_up_message(self, text):
return re.sub(r'<at>.+</at>', '', text).strip()

def format_access_request_params(self, resource_name, sender_nick, request_id):
resource_name = f'**{resource_name}**'
sender_nick = f'**{sender_nick}**'
request_id = f'**{request_id}**'
return resource_name, sender_nick, request_id
def format_access_request_params(self, resource_name, sender_nick):
return f'**{resource_name}**', f'**{sender_nick}**'

def format_strikethrough(self, text):
return r"~~" + text + r"~~"
Expand Down
7 changes: 2 additions & 5 deletions plugins/sdm/lib/platform/slack_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,8 @@ def get_user_nick(self, approver):
def clean_up_message(self, text):
return text

def format_access_request_params(self, resource_name, sender_nick, request_id):
resource_name = r"\`" + resource_name + r"\`"
sender_nick = r"\`" + sender_nick + r"\`"
request_id = r"\`" + request_id + r"\`"
return resource_name, sender_nick, request_id
def format_access_request_params(self, resource_name, sender_nick):
return r"\`" + resource_name + r"\`", r"\`" + sender_nick + r"\`"

def format_strikethrough(self, text):
return r"~" + text + r"~"
Expand Down
1 change: 1 addition & 0 deletions requirements/common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
strongdm
datetime
errbot
slackclient
slack-bolt
shortuuid
fuzzywuzzy
Expand Down