Introduce org uuid (#947)

* Introduce org uuid

* Rename uuid_with_org_id to uuid_with_org_uuid

Co-authored-by: Joey Orlando <joey.orlando@grafana.com>
This commit is contained in:
Innokentii Konstantinov 2022-12-06 22:42:58 +08:00 committed by GitHub
parent e52af4858c
commit 7341641b3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 65 additions and 35 deletions

View file

@ -51,7 +51,7 @@ class AlertGroupTelegramRenderer(AlertGroupBaseRenderer):
status_verbose = self.alert_group.get_acknowledge_text()
# First line in the invisible link with id of organization.
# It is needed to add info about organization to the telegram message for the oncall-gateway.
text = f"<a href='{self.alert_group.channel.organization.web_link_with_id}'>&#8205;</a>"
text = f"<a href='{self.alert_group.channel.organization.web_link_with_uuid}'>&#8205;</a>"
text += f"{status_emoji} #{self.alert_group.inside_organization_number}, {title}\n"
text += f"{status_verbose}, alerts: {alerts_count_str}\n"
text += f"Source: {self.alert_group.channel.short_name}\n"

View file

@ -67,7 +67,7 @@ class GetTelegramVerificationCode(APIView):
bot_username = telegram_client.api_client.username
bot_link = f"https://t.me/{bot_username}"
return Response(
{"telegram_code": str(new_code.uuid_with_org_id), "bot_link": bot_link}, status=status.HTTP_200_OK
{"telegram_code": str(new_code.uuid_with_org_uuid), "bot_link": bot_link}, status=status.HTTP_200_OK
)

View file

@ -374,7 +374,7 @@ class UserView(
bot_link = f"https://t.me/{bot_username}"
return Response(
{"telegram_code": str(new_code.uuid_with_org_id), "bot_link": bot_link}, status=status.HTTP_200_OK
{"telegram_code": str(new_code.uuid_with_org_uuid), "bot_link": bot_link}, status=status.HTTP_200_OK
)
@action(detail=True, methods=["post"])

View file

@ -24,8 +24,8 @@ class TelegramChannelVerificationCode(models.Model):
return self.datetime + timezone.timedelta(days=1) < timezone.now()
@property
def uuid_with_org_id(self) -> str:
return f"{self.organization.public_primary_key}_{self.uuid}"
def uuid_with_org_uuid(self) -> str:
return f"{self.organization.uuid}_{self.uuid}"
@classmethod
def uuid_without_org_id(cls, verification_code: str) -> str:

View file

@ -22,8 +22,8 @@ class TelegramVerificationCode(models.Model):
return self.datetime + timezone.timedelta(days=1) < timezone.now()
@property
def uuid_with_org_id(self) -> str:
return f"{self.user.organization.public_primary_key}_{self.uuid}"
def uuid_with_org_uuid(self) -> str:
return f"{self.user.organization.uuid}_{self.uuid}"
@classmethod
def uuid_without_org_id(cls, verification_code: str) -> str:

View file

@ -83,10 +83,10 @@ class TelegramKeyboardRenderer:
callback_data_args = [self.alert_group.pk, action.value]
if action_data is not None:
callback_data_args.append(action_data)
# Add org id with 'x-oncall-org-id' prefix to callback data.
# Add org id with 'oncall-uuid' prefix to callback data.
# It's a workaroung to pass org_id to the oncall-gateway while proxying requests.
# TODO: switch to json str instead of ':' separated string.
callback_data_args.append(f"x-oncall-org-id{self.alert_group.channel.organization.public_primary_key}")
callback_data_args.append(f"oncall-uuid{self.alert_group.channel.organization.uuid}")
button = InlineKeyboardButton(text=text, callback_data=CallbackQueryFactory.encode_data(*callback_data_args))
return button

View file

@ -49,27 +49,27 @@ def test_actions_keyboard_alerting(make_organization, make_alert_receive_channel
[
InlineKeyboardButton(
text="Acknowledge",
callback_data=f"{alert_group.pk}:acknowledge:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:acknowledge:oncall-uuid{organization.uuid}",
)
],
[
InlineKeyboardButton(
text="Resolve",
callback_data=f"{alert_group.pk}:resolve:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:resolve:oncall-uuid{organization.uuid}",
)
],
[
InlineKeyboardButton(
text="🔕 forever",
callback_data=f"{alert_group.pk}:silence:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:silence:oncall-uuid{organization.uuid}",
),
InlineKeyboardButton(
text="... for 1h",
callback_data=f"{alert_group.pk}:silence:3600:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:silence:3600:oncall-uuid{organization.uuid}",
),
InlineKeyboardButton(
text="... for 4h",
callback_data=f"{alert_group.pk}:silence:14400:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:silence:14400:oncall-uuid{organization.uuid}",
),
],
]
@ -97,13 +97,13 @@ def test_actions_keyboard_acknowledged(
[
InlineKeyboardButton(
text="Unacknowledge",
callback_data=f"{alert_group.pk}:unacknowledge:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:unacknowledge:oncall-uuid{organization.uuid}",
)
],
[
InlineKeyboardButton(
text="Resolve",
callback_data=f"{alert_group.pk}:resolve:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:resolve:oncall-uuid{organization.uuid}",
)
],
]
@ -131,7 +131,7 @@ def test_actions_keyboard_resolved(
[
InlineKeyboardButton(
text="Unresolve",
callback_data=f"{alert_group.pk}:unresolve:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:unresolve:oncall-uuid{organization.uuid}",
)
],
]
@ -159,19 +159,19 @@ def test_actions_keyboard_silenced(
[
InlineKeyboardButton(
text="Acknowledge",
callback_data=f"{alert_group.pk}:acknowledge:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:acknowledge:oncall-uuid{organization.uuid}",
)
],
[
InlineKeyboardButton(
text="Resolve",
callback_data=f"{alert_group.pk}:resolve:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:resolve:oncall-uuid{organization.uuid}",
)
],
[
InlineKeyboardButton(
text="Unsilence",
callback_data=f"{alert_group.pk}:unsilence:x-oncall-org-id{organization.public_primary_key}",
callback_data=f"{alert_group.pk}:unsilence:oncall-uuid{organization.uuid}",
)
],
]

View file

@ -72,7 +72,7 @@ def test_alert_group_message(make_organization, make_alert_receive_channel, make
renderer = TelegramMessageRenderer(alert_group=alert_group)
text = renderer.render_alert_group_message()
assert text == (
f"<a href='{organization.web_link_with_id}'>&#8205;</a>🔴 #{alert_group.inside_organization_number}, {alert_receive_channel.config.tests['telegram']['title']}\n"
f"<a href='{organization.web_link_with_uuid}'>&#8205;</a>🔴 #{alert_group.inside_organization_number}, {alert_receive_channel.config.tests['telegram']['title']}\n"
"Alerting, alerts: 1\n"
"Source: Test integration - Grafana\n"
f"{alert_group.web_link}\n\n"
@ -156,7 +156,7 @@ def test_personal_message(
text = renderer.render_personal_message()
assert text == (
f"<a href='{organization.web_link_with_id}'>&#8205;</a>🟠 #{alert_group.inside_organization_number}, {alert_receive_channel.config.tests['telegram']['title']}\n"
f"<a href='{organization.web_link_with_uuid}'>&#8205;</a>🟠 #{alert_group.inside_organization_number}, {alert_receive_channel.config.tests['telegram']['title']}\n"
f"Acknowledged by {user_name}, alerts: 1\n"
"Source: Test integration - Grafana\n"
f"{alert_group.web_link}\n\n"

View file

@ -17,7 +17,7 @@ def test_user_verification_handler_process_update_another_account_already_linked
user_2 = make_user_for_organization(organization)
code = make_telegram_verification_code(user_2)
connector, created = TelegramVerificationCode.verify_user(code.uuid_with_org_id, chat_id, "nickname")
connector, created = TelegramVerificationCode.verify_user(code.uuid_with_org_uuid, chat_id, "nickname")
assert created
assert connector.telegram_chat_id == chat_id
@ -38,7 +38,7 @@ def test_user_verification_handler_process_update_user_already_linked(
other_chat_id = 321
code = make_telegram_verification_code(user_1)
connector, created = TelegramVerificationCode.verify_user(code.uuid_with_org_id, other_chat_id, "nickname")
connector, created = TelegramVerificationCode.verify_user(code.uuid_with_org_uuid, other_chat_id, "nickname")
assert created is False
assert connector.user == user_1

View file

@ -61,8 +61,8 @@ class ButtonPressHandler(UpdateHandler):
has_permission = user_is_authorized(user, [RBACPermission.Permissions.CHATOPS_WRITE])
return user.organization == alert_group.channel.organization and has_permission
@staticmethod
def _get_action_context(data: str) -> ActionContext:
@classmethod
def _get_action_context(cls, data: str) -> ActionContext:
args = CallbackQueryFactory.decode_data(data)
alert_group_pk = args[0]
@ -71,10 +71,16 @@ class ButtonPressHandler(UpdateHandler):
action_name = args[1]
action = Action(action_name)
action_data = args[2] if len(args) >= 3 and not args[2].startswith("x-oncall-org-id") else None
action_data = args[2] if len(args) >= 3 and not cls._is_oncall_identifier(args[2]) else None
return ActionContext(alert_group=alert_group, action=action, action_data=action_data)
@staticmethod
def _is_oncall_identifier(string: str) -> bool:
# determines if piece of data passed via callback_data is oncall_identifier
# x-oncall-org-id is kept here for backward compatibility.
return string.startswith("x-oncall-org-id") or string.startswith("oncall-uuid")
@staticmethod
def _map_action_context_to_fn(action_context: ActionContext) -> Tuple[Callable, dict]:
action_to_fn = {

View file

@ -1,7 +1,8 @@
import re
from typing import List, Union
TELEGRAM_VERIFICATION_CODE_REGEX = "^[A-Z0-9]*_[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
uuid_regex = "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"
TELEGRAM_VERIFICATION_CODE_REGEX = f"^{uuid_regex}_{uuid_regex}$"
def is_verification_message(text: str) -> bool:

View file

@ -0,0 +1,19 @@
# Generated by Django 3.2.16 on 2022-12-05 07:00
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('user_management', '0005_rbac_permissions'),
]
operations = [
migrations.AddField(
model_name='organization',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, editable=False),
),
]

View file

@ -1,5 +1,6 @@
import logging
import typing
import uuid
from urllib.parse import urljoin
from django.apps import apps
@ -129,6 +130,9 @@ class Organization(MaintainableObject):
# Slack specific field with general log channel id
general_log_channel_id = models.CharField(max_length=100, null=True, default=None)
# uuid used to unuqie identify organization in different clusters
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
# Organization Settings configured from slack
(
ACKNOWLEDGE_REMIND_NEVER,
@ -282,9 +286,9 @@ class Organization(MaintainableObject):
return urljoin(self.grafana_url, "a/grafana-oncall-app/")
@property
def web_link_with_id(self):
# It's a workaround to pass org id to the oncall gateway while proxying telegram requests
return urljoin(self.grafana_url, f"a/grafana-oncall-app/?x-oncall-org-id={self.public_primary_key}")
def web_link_with_uuid(self):
# It's a workaround to pass some unique identifier to the oncall gateway while proxying telegram requests
return urljoin(self.grafana_url, f"a/grafana-oncall-app/?oncall-uuid={self.uuid}")
def __str__(self):
return f"{self.pk}: {self.org_title}"

View file

@ -71,8 +71,8 @@ class OnCallGatewayAPIClient:
response = self._post(url=self._slack_connectors_url, json=d)
response_data = response.json()
return (
OnCallConnector(
response_data["oncall_org_id"],
SlackConnector(
response_data["slack_team_id"],
response_data["backend"],
),
response,

View file

@ -39,13 +39,13 @@ const TelegramInfo = observer((_props: TelegramInfoProps) => {
<>
{telegramConfigured || !store.hasFeature(AppFeature.LiveSettings) ? (
<VerticalGroup>
<Text.Title level={5}>Connect personal Telegram</Text.Title>
{/* <Text.Title level={5}>Connect personal Telegram</Text.Title>
<Block bordered withBackground className={cx('automatic-connect-telegram-block')}>
<Text type="secondary">Connect Telegram automatically</Text>
<a href={`${botLink}/?start=${verificationCode}`} target="_blank" rel="noreferrer">
<Button size="sm">Connect account</Button>
</a>
</Block>
</Block> */}
<Text.Title level={5}>Manual connection</Text.Title>
<Text type="secondary">