# What this PR does
fix the following error caused by too short varchar length of database
fields `access_token` and `bot_access_token` in table
`slack_slackteamidentity`
```
2023-09-12 18:34:07.448 | django.db.utils.DataError: value too long for type character varying(100) |
-- | -- | --
| | 2023-09-12 18:34:07.448 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.448 | return self.cursor.execute(sql, params) |
| | 2023-09-12 18:34:07.448 | File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute |
| | 2023-09-12 18:34:07.448 | raise dj_exc_value.with_traceback(traceback) from exc_value |
| | 2023-09-12 18:34:07.448 | File "/usr/local/lib/python3.11/site-packages/django/db/utils.py", line 90, in __exit__ |
| | 2023-09-12 18:34:07.448 | with self.db.wrap_database_errors: |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 79, in _execute |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return executor(sql, params, many, context) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 66, in execute |
| | 2023-09-12 18:34:07.447 | cursor.execute(sql, params) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1175, in execute_sql |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | cursor = super().execute_sql(result_type) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1559, in execute_sql |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return query.get_compiler(self.db).execute_sql(CURSOR) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 802, in _update |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return filtered._update(values) > 0 |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 912, in _do_update |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | updated = self._do_update(base_qs, using, pk_val, values, update_fields, |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 858, in _save_table |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | updated = self._save_table( |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 776, in save_base |
| | 2023-09-12 18:34:07.447 | self.save_base(using=using, force_insert=force_insert, |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 739, in save |
| | 2023-09-12 18:34:07.447 | self.save() |
| | 2023-09-12 18:34:07.447 | File "/etc/app/apps/slack/models/slack_team_identity.py", line 72, in update_oauth_fields |
| | 2023-09-12 18:34:07.447 | slack_team_identity.update_oauth_fields(user, organization, response) |
| | 2023-09-12 18:34:07.447 | File "/etc/app/apps/social_auth/pipeline.py", line 102, in populate_slack_identities |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | result = func(*args, **out) or {} |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_core/backends/base.py", line 118, in run_pipeline |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_core/backends/base.py", line 86, in pipeline |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return self.pipeline(pipeline, *args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_core/backends/base.py", line 83, in authenticate |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | user = backend.authenticate(request, **credentials) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/contrib/auth/__init__.py", line 76, in authenticate |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return func(*func_args, **func_kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/views/decorators/debug.py", line 42, in sensitive_variables_wrapper |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return authenticate(*args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_django/strategy.py", line 105, in authenticate |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return self.strategy.authenticate(*args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/etc/app/apps/social_auth/backends.py", line 100, in do_auth |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return func(*args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_core/utils.py", line 253, in wrapper |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return self.do_auth(access_token, response=response, *args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/etc/app/apps/social_auth/backends.py", line 88, in auth_complete |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return func(*args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_core/utils.py", line 253, in wrapper |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return self.auth_complete(*args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_core/backends/base.py", line 39, in complete |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | user = backend.complete(user=user, *args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_core/actions.py", line 49, in do_complete |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | result = do_complete( |
| | 2023-09-12 18:34:07.447 | File "/etc/app/apps/api/views/auth.py", line 52, in overridden_complete_slack_auth |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return func(request, backend, *args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/social_django/utils.py", line 46, in wrapper |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return view_func(*args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | response = view_func(request, *args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return func(*args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/rest_framework/decorators.py", line 50, in handler |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | response = handler(request, *args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 506, in dispatch |
| | 2023-09-12 18:34:07.447 | raise exc |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception |
| | 2023-09-12 18:34:07.447 | self.raise_uncaught_exception(exc) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 469, in handle_exception |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | response = self.handle_exception(exc) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 509, in dispatch |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return self.dispatch(request, *args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 70, in view |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return view_func(*args, **kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | response = wrapped_callback(request, *callback_args, **callback_kwargs) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 181, in _get_response |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | response = get_response(request) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 47, in inner |
| | 2023-09-12 18:34:07.447 | Traceback (most recent call last): |
| | 2023-09-12 18:34:07.447 | |
| | 2023-09-12 18:34:07.447 | The above exception was the direct cause of the following exception: |
| | 2023-09-12 18:34:07.447 | |
| | 2023-09-12 18:34:07.447 | |
| | 2023-09-12 18:34:07.447 | psycopg2.errors.StringDataRightTruncation: value too long for type character varying(100) |
| | 2023-09-12 18:34:07.447 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| | 2023-09-12 18:34:07.447 | return self.cursor.execute(sql, params) |
| | 2023-09-12 18:34:07.447 | File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute |
| | 2023-09-12 18:34:07.447 | Traceback (most recent call last): |
| | 2023-09-12 18:34:07.447 | 2023-09-12 10:34:07 source=engine:app google_trace_id=50a15245db9b828e0ab33e8d115dd10a/3838538413005063100 logger=django.request Internal Server Error: /api/internal/v1/complete/slack-install-free/
```
---------
Co-authored-by: Vadim Stepanov <vadimkerr@gmail.com>
151 lines
5.9 KiB
Python
151 lines
5.9 KiB
Python
import logging
|
|
import typing
|
|
|
|
from django.db import models
|
|
from django.db.models import JSONField
|
|
|
|
from apps.api.permissions import RBACPermission
|
|
from apps.slack.client import SlackClient
|
|
from apps.slack.constants import SLACK_INVALID_AUTH_RESPONSE, SLACK_WRONG_TEAM_NAMES
|
|
from apps.slack.errors import (
|
|
SlackAPIChannelNotFoundError,
|
|
SlackAPIFetchMembersFailedError,
|
|
SlackAPIInvalidAuthError,
|
|
SlackAPITokenError,
|
|
)
|
|
from apps.user_management.models.user import User
|
|
from common.insight_log.chatops_insight_logs import ChatOpsEvent, ChatOpsTypePlug, write_chatops_insight_log
|
|
|
|
if typing.TYPE_CHECKING:
|
|
from django.db.models.manager import RelatedManager
|
|
|
|
from apps.user_management.models import Organization
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class SlackTeamIdentity(models.Model):
|
|
organizations: "RelatedManager['Organization']"
|
|
|
|
id = models.AutoField(primary_key=True)
|
|
slack_id = models.CharField(max_length=100)
|
|
cached_name = models.CharField(max_length=100, null=True, default=None)
|
|
cached_app_id = models.CharField(max_length=100, null=True, default=None)
|
|
access_token = models.CharField(max_length=255, null=True, default=None)
|
|
bot_user_id = models.CharField(max_length=100, null=True, default=None)
|
|
bot_access_token = models.CharField(max_length=255, null=True, default=None)
|
|
oauth_scope = models.TextField(max_length=30000, null=True, default=None)
|
|
detected_token_revoked = models.DateTimeField(null=True, default=None, verbose_name="Deleted At")
|
|
is_profile_populated = models.BooleanField(default=False)
|
|
datetime = models.DateTimeField(auto_now_add=True)
|
|
installed_via_granular_permissions = models.BooleanField(default=True)
|
|
|
|
installed_by = models.ForeignKey("SlackUserIdentity", on_delete=models.PROTECT, null=True, default=None)
|
|
|
|
last_populated = models.DateTimeField(null=True, default=None)
|
|
|
|
cached_bot_id = models.CharField(max_length=100, null=True, default=None)
|
|
|
|
# response after oauth.access. This field is used to reinstall app to another OnCall workspace
|
|
cached_reinstall_data = JSONField(null=True, default=None)
|
|
|
|
class Meta:
|
|
ordering = ("datetime",)
|
|
|
|
def __str__(self):
|
|
return f"{self.pk}: {self.name}"
|
|
|
|
def update_oauth_fields(self, user, organization, reinstall_data):
|
|
logger.info(f"updated oauth_fields for sti {self.pk}")
|
|
from apps.slack.models import SlackUserIdentity
|
|
|
|
organization.slack_team_identity = self
|
|
organization.save(update_fields=["slack_team_identity"])
|
|
slack_user_identity, _ = SlackUserIdentity.objects.get_or_create(
|
|
slack_id=reinstall_data["authed_user"]["id"],
|
|
slack_team_identity=self,
|
|
)
|
|
user.slack_user_identity = slack_user_identity
|
|
user.save(update_fields=["slack_user_identity"])
|
|
self.bot_access_token = reinstall_data["access_token"]
|
|
self.bot_user_id = reinstall_data["bot_user_id"]
|
|
self.oauth_scope = reinstall_data["scope"]
|
|
self.cached_name = reinstall_data["team"]["name"]
|
|
self.access_token = reinstall_data["authed_user"]["access_token"]
|
|
self.installed_by = slack_user_identity
|
|
self.cached_reinstall_data = None
|
|
self.installed_via_granular_permissions = True
|
|
self.save()
|
|
write_chatops_insight_log(
|
|
author=user, event_name=ChatOpsEvent.WORKSPACE_CONNECTED, chatops_type=ChatOpsTypePlug.SLACK.value
|
|
)
|
|
|
|
def get_cached_channels(self, search_term=None, slack_id=None):
|
|
queryset = self.cached_channels
|
|
if search_term is not None:
|
|
queryset = queryset.filter(name__startswith=search_term)
|
|
if slack_id is not None:
|
|
queryset = queryset.filter(slack_id=slack_id)
|
|
return queryset.all()
|
|
|
|
@property
|
|
def bot_id(self):
|
|
if self.cached_bot_id is None:
|
|
sc = SlackClient(self)
|
|
auth = sc.auth_test()
|
|
self.cached_bot_id = auth.get("bot_id")
|
|
self.save(update_fields=["cached_bot_id"])
|
|
return self.cached_bot_id
|
|
|
|
@property
|
|
def members(self):
|
|
sc = SlackClient(self)
|
|
|
|
next_cursor = None
|
|
members = []
|
|
while next_cursor != "" or next_cursor is None:
|
|
result = sc.users_list(cursor=next_cursor, team=self)
|
|
next_cursor = result["response_metadata"]["next_cursor"]
|
|
members += result["members"]
|
|
|
|
return members
|
|
|
|
@property
|
|
def name(self):
|
|
if self.cached_name is None or self.cached_name in SLACK_WRONG_TEAM_NAMES:
|
|
try:
|
|
sc = SlackClient(self)
|
|
result = sc.team_info()
|
|
self.cached_name = result["team"]["name"]
|
|
self.save()
|
|
except SlackAPIInvalidAuthError:
|
|
self.cached_name = SLACK_INVALID_AUTH_RESPONSE
|
|
self.save()
|
|
return self.cached_name
|
|
|
|
@property
|
|
def app_id(self):
|
|
if not self.cached_app_id:
|
|
sc = SlackClient(self)
|
|
result = sc.bots_info(bot=self.bot_id)
|
|
app_id = result["bot"]["app_id"]
|
|
self.cached_app_id = app_id
|
|
self.save(update_fields=["cached_app_id"])
|
|
return self.cached_app_id
|
|
|
|
def get_users_from_slack_conversation_for_organization(self, channel_id, organization):
|
|
sc = SlackClient(self)
|
|
members = self.get_conversation_members(sc, channel_id)
|
|
|
|
return organization.users.filter(
|
|
slack_user_identity__slack_id__in=members,
|
|
**User.build_permissions_query(RBACPermission.Permissions.CHATOPS_WRITE, organization),
|
|
)
|
|
|
|
def get_conversation_members(self, slack_client: SlackClient, channel_id: str):
|
|
try:
|
|
return slack_client.paginated_api_call(
|
|
"conversations_members", paginated_key="members", channel=channel_id
|
|
)["members"]
|
|
except (SlackAPITokenError, SlackAPIFetchMembersFailedError, SlackAPIChannelNotFoundError):
|
|
return []
|