feat: instruct создаёт SD заявку при отсутствии 2FA

This commit is contained in:
Денис Кривоченко
2026-07-23 01:52:45 +07:00
parent 3fa7543477
commit 04430e56ef
2 changed files with 44 additions and 4 deletions
+38 -1
View File
@@ -8,8 +8,12 @@ from utils.texts import (
) )
import logging import logging
import asyncio import asyncio
import httpx
import json
import os
from trueconf import Router, Message from trueconf import Router, Message
from trueconf.types import FSInputFile
from utils.states import get_state, set_state, clear_state from utils.states import get_state, set_state, clear_state
@@ -18,8 +22,26 @@ from utils.stats_logger import log_menu_stats
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
async def sd_create_ticket(login: str, subject: str, description: str) -> str:
"""Создать заявку в SD."""
from config.config import SD_URL, SD_TOKEN
headers = {"authtoken": SD_TOKEN, "Accept": "application/vnd.manageengine.sdp.v3+json"}
endpoint = f"{SD_URL}/api/v3/requests"
html_desc = f"<p>{description}</p><br><hr><p style='color:#555;font-size:12px;'><i>Создано через TrueConf КЛЕВЕР</i></p>"
payload = {"request": {"subject": subject, "description": html_desc, "requester": {"email_id": None}, "udf_fields": {}}}
async with httpx.AsyncClient(verify=False) as client:
resp = await client.post(endpoint, headers=headers, data={"input_data": json.dumps(payload)}, timeout=15.0)
data = resp.json()
if data.get("response_status", {}).get("status_code") == 2000:
return str(data.get("request", {}).get("id"))
return None
router = Router() router = Router()
# Путь к файлу инструкции Trueconf
TRUECONF_INSTRUCT_PATH = os.path.join(os.path.dirname(__file__), "Проверка_наличия_и_авторизация_на_мобильном_устройстве_Trueconf.docx")
def get_user_cn(user_id: str) -> str | None: def get_user_cn(user_id: str) -> str | None:
""" """
@@ -94,8 +116,23 @@ async def instruct_router_handler(msg: Message):
log_menu_stats(user_id, "Инструкции", "Trueconf") log_menu_stats(user_id, "Инструкции", "Trueconf")
set_state(user_id, "INSTRUCT_TRUECONF_VIEW") set_state(user_id, "INSTRUCT_TRUECONF_VIEW")
cn = get_user_cn(user_id) cn = get_user_cn(user_id)
text = INSTRUCT_TRUECONF_WITH_AD(EMOJI_DIGITS, cn) login = user_id.split("@")[0] if "@" in user_id else user_id
ticket_id = None
try:
from utils.ad_checker import check_group_membership
result = check_group_membership(cn, "2FA")
if not result.get("in_group", True):
subject = f"Запрос доступа к Trueconf"
description = f"Пользователь {cn} ({user_id}) запросил инструкцию по Trueconf, но не имеет группы 2FA. Требуется выдача доступа."
ticket_id = await sd_create_ticket(login, subject, description)
logger.info(f"SD ticket created for {cn}: #{ticket_id}")
except Exception as e:
logger.error(f"Ошибка создания SD заявки: {e}", exc_info=True)
ticket_id = None
text = INSTRUCT_TRUECONF_WITH_AD(EMOJI_DIGITS, cn, ticket_id)
await msg.answer(text, parse_mode="html") await msg.answer(text, parse_mode="html")
if os.path.isfile(TRUECONF_INSTRUCT_PATH):
await msg.answer_document(FSInputFile(TRUECONF_INSTRUCT_PATH))
elif cmd == "2": elif cmd == "2":
log_menu_stats(user_id, "Инструкции", "Почта") log_menu_stats(user_id, "Инструкции", "Почта")
set_state(user_id, "INSTRUCT_EMAIL_VIEW") set_state(user_id, "INSTRUCT_EMAIL_VIEW")
+6 -3
View File
@@ -595,19 +595,21 @@ def _instruct_trueconf(emojis: dict) -> str:
) )
def _instruct_trueconf_with_ad(emojis: dict, cn: str) -> str: def _instruct_trueconf_with_ad(emojis: dict, cn: str, ticket_id: str = None) -> str:
""" """
Текст Trueconf с проверкой группы 2FA. Текст Trueconf с проверкой группы 2FA.
cn — CN пользователя из AD. cn — CN пользователя из AD.
ticket_id — номер созданной заявки в SD.
""" """
from utils.ad_checker import check_group_membership from utils.ad_checker import check_group_membership
result = check_group_membership(cn, "2FA") result = check_group_membership(cn, "2FA")
if not result["found"]: if not result["found"]:
ticket_part = f"\n\n🎫 <b>Заявка #{ticket_id} создана!</b>" if ticket_id else ""
return ( return (
f"🖥 <b>Инструкция по Trueconf</b>\n\n" f"🖥 <b>Инструкция по Trueconf</b>\n\n"
f"Пользователь {cn} не найден в Active Directory.\n\n" f"Пользователь {cn} не найден в Active Directory.{ticket_part}\n\n"
f"<i>{emojis['9']} — Назад\n{emojis['0']} — В главное меню</i>" f"<i>{emojis['9']} — Назад\n{emojis['0']} — В главное меню</i>"
) )
@@ -622,10 +624,11 @@ def _instruct_trueconf_with_ad(emojis: dict, cn: str) -> str:
f"<i>{emojis['9']} — Назад\n{emojis['0']} — В главное меню</i>" f"<i>{emojis['9']} — Назад\n{emojis['0']} — В главное меню</i>"
) )
else: else:
ticket_part = f"\n\n🎫 <b>Заявка #{ticket_id} создана!</b>" if ticket_id else ""
body = ( body = (
f"🖥 <b>Инструкция по Trueconf</b>\n\n" f"🖥 <b>Инструкция по Trueconf</b>\n\n"
f"❌ <b>Доступ к Trueconf запрещён.</b>\n\n" f"❌ <b>Доступ к Trueconf запрещён.</b>\n\n"
f"У вас нет группы 2FA в Active Directory.\n\n" f"У вас нет группы 2FA в Active Directory.{ticket_part}\n\n"
f"Детали:\n" f"Детали:\n"
f"• CN: {result['cn']}\n" f"• CN: {result['cn']}\n"
f"• Групп 2FA: 0\n\n" f"• Групп 2FA: 0\n\n"