mirror of
https://gitverse.ru/kpa39l/icq.git
synced 2026-09-29 09:55:11 +00:00
OpenSpec: разнести openspec по проектам
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Тест авторизации на Prosody 13.0 (test.nixg.ru:15222)."""
|
||||
import asyncio, sys, logging, ssl
|
||||
import slixmpp
|
||||
|
||||
logging.basicConfig(level=logging.WARNING, format='%(levelname)s %(name)s: %(message)s')
|
||||
|
||||
async def try_login(jid, password, expect_ok=True):
|
||||
class T(slixmpp.ClientXMPP):
|
||||
def __init__(self):
|
||||
super().__init__(jid, password)
|
||||
self.ok = None
|
||||
# Отключаем проверку сертификата (тестовый стенд, самоподписанный)
|
||||
self.ssl_context = ssl.create_default_context()
|
||||
self.ssl_context.check_hostname = False
|
||||
self.ssl_context.verify_mode = ssl.CERT_NONE
|
||||
self.add_event_handler('session_start', self.on_start)
|
||||
self.add_event_handler('failed_auth', self.on_fail)
|
||||
self.add_event_handler('disconnected', self.on_disc)
|
||||
self.add_event_handler('connected', self.on_conn)
|
||||
def on_conn(self, ev): print(f" [connected] {jid}")
|
||||
def on_start(self, ev):
|
||||
self.ok = True
|
||||
print(f" [AUTH OK] {jid} — session_start, full JID={self.boundjid}")
|
||||
self.disconnect()
|
||||
def on_fail(self, ev):
|
||||
self.ok = False
|
||||
print(f" [AUTH FAILED] {jid} — incorrect credentials rejected correctly")
|
||||
self.disconnect()
|
||||
def on_disc(self, ev):
|
||||
if self.ok is None:
|
||||
print(f" [DISCONNECTED before auth] {jid} reason={ev}")
|
||||
x = T()
|
||||
# Явно указываем хост/порт тестового сервера (15222 = нестандартный c2s)
|
||||
try:
|
||||
await x.connect('127.0.0.1', 15222)
|
||||
await asyncio.wait_for(x.wait_until('session_start' if expect_ok else 'failed_auth'), 15)
|
||||
except asyncio.TimeoutError:
|
||||
print(f" [TIMEOUT] {jid} — no auth result in 15s")
|
||||
x.disconnect()
|
||||
except Exception as e:
|
||||
print(f" [ERROR] {jid}: {type(e).__name__}: {e}")
|
||||
finally:
|
||||
try: x.disconnect()
|
||||
except: pass
|
||||
|
||||
async def main():
|
||||
print("=== 1. Верный пароль: testuser1@test.nixg.ru ===")
|
||||
await try_login("testuser1@test.nixg.ru", "pass12345", True)
|
||||
print("=== 2. Верный пароль: testuser2@test.nixg.ru ===")
|
||||
await try_login("testuser2@test.nixg.ru", "pass12345", True)
|
||||
print("=== 3. Админ ===")
|
||||
await try_login("admin@test.nixg.ru", "adminpass123", True)
|
||||
print("=== 4. НЕВЕРНЫЙ пароль (должен отказать) ===")
|
||||
await try_login("testuser1@test.nixg.ru", "WRONG", False)
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Тест авторизации с подробным логом — одна попытка."""
|
||||
import asyncio, logging, ssl
|
||||
import slixmpp
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(name)s: %(message)s', datefmt='%H:%M:%S')
|
||||
|
||||
class T(slixmpp.ClientXMPP):
|
||||
def __init__(self):
|
||||
super().__init__("testuser1@test.nixg.ru", "pass12345")
|
||||
self.ssl_context = ssl.create_default_context()
|
||||
self.ssl_context.check_hostname = False
|
||||
self.ssl_context.verify_mode = ssl.CERT_NONE
|
||||
self.add_event_handler('session_start', self.on_start)
|
||||
self.add_event_handler('failed_auth', self.on_fail)
|
||||
self.add_event_handler('disconnected', self.on_disc)
|
||||
self.add_event_handler('connected', self.on_conn)
|
||||
self.add_event_handler('ssl_invalid_cert', self.on_ssl)
|
||||
def on_conn(self, ev): print(">>> connected")
|
||||
def on_ssl(self, ev): print(">>> ssl_invalid_cert:", ev)
|
||||
def on_start(self, ev): print(">>> AUTH OK, boundjid=", self.boundjid); self.disconnect()
|
||||
def on_fail(self, ev): print(">>> AUTH FAILED"); self.disconnect()
|
||||
def on_disc(self, ev): print(">>> disconnected:", ev)
|
||||
|
||||
async def main():
|
||||
x = T()
|
||||
try:
|
||||
await asyncio.wait_for(x.connect('127.0.0.1', 15222), 20)
|
||||
await asyncio.wait_for(x.wait_until('session_start'), 20)
|
||||
except asyncio.TimeoutError:
|
||||
print(">>> TIMEOUT waiting auth")
|
||||
except Exception as e:
|
||||
print(">>> EXC:", type(e).__name__, e)
|
||||
finally:
|
||||
try: x.disconnect()
|
||||
except: pass
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Функциональный тест mod_privilege (XEP-0356) на Prosody 13.0.
|
||||
|
||||
Подключается как внешний компонент telegram.test.nixg.ru (XEP-0114),
|
||||
затем от имени компонента делает привилегированный запрос roster
|
||||
пользователя testuser1@test.nixg.ru.
|
||||
Если mod_privilege работает — компонент получит roster.
|
||||
Если нет — сервер ответит forbidden/not-allowed.
|
||||
"""
|
||||
import asyncio, logging, ssl
|
||||
import slixmpp
|
||||
from slixmpp import ComponentXMPP
|
||||
from slixmpp.xmlstream import ET
|
||||
from slixmpp.xmlstream.handler import Callback
|
||||
from slixmpp.xmlstream.matcher import MatchXPath
|
||||
|
||||
logging.basicConfig(level=logging.WARNING, format='%(levelname)s %(name)s: %(message)s')
|
||||
logging.getLogger('slixmpp.xmlstream.xmlstream').setLevel(logging.DEBUG)
|
||||
|
||||
async def main():
|
||||
comp = ComponentXMPP("telegram.test.nixg.ru", "test-secret-telegram-123",
|
||||
"test.nixg.ru")
|
||||
comp.ssl_context = ssl.create_default_context()
|
||||
comp.ssl_context.check_hostname = False
|
||||
comp.ssl_context.verify_mode = ssl.CERT_NONE
|
||||
|
||||
result = {"done": False}
|
||||
|
||||
def on_iq(ev):
|
||||
# Callback на {jabber:component:accept}iq присылает строку XML, парсим
|
||||
if isinstance(ev, str):
|
||||
ev = ET.fromstring(ev)
|
||||
print(f">>> on_iq fired: type={ev.get('type')}, id={ev.get('id')}")
|
||||
if ev.get('type') in ('result', 'error') and ev.get('id') == 'priv-roster-1':
|
||||
result['done'] = True
|
||||
if ev.get('type') == 'result':
|
||||
print(">>> PRIVILEGE OK: mod_privilege ответил result (roster testuser1)")
|
||||
# достаём все <item> из <query>
|
||||
for q in ev.findall('{jabber:iq:roster}query'):
|
||||
for it in q.findall('{jabber:iq:roster}item'):
|
||||
print(f" - {it.get('jid')} (sub={it.get('subscription')})")
|
||||
if not ev.findall('{jabber:iq:roster}query/*'):
|
||||
print(" (roster пуст — у testuser1 нет контактов)")
|
||||
else:
|
||||
conds = ev.findall('{urn:ietf:params:xml:ns:xmpp-stanzas}*')
|
||||
cond = conds[0].tag.split('}')[1] if conds else '?'
|
||||
print(f">>> PRIVILEGE FAILED: {cond}")
|
||||
comp.disconnect()
|
||||
|
||||
comp.add_event_handler('iq', on_iq)
|
||||
|
||||
def on_session_start(ev):
|
||||
print(">>> component connected as telegram.test.nixg.ru")
|
||||
# Привилегированный запрос: получить roster testuser1
|
||||
iq = comp.Iq()
|
||||
iq['id'] = 'priv-roster-1'
|
||||
iq['type'] = 'get'
|
||||
iq['to'] = 'testuser1@test.nixg.ru'
|
||||
iq['from'] = 'telegram.test.nixg.ru'
|
||||
iq.append(ET.Element('{jabber:iq:roster}query'))
|
||||
comp.send(iq)
|
||||
|
||||
comp.add_event_handler('session_start', on_session_start)
|
||||
comp.add_event_handler('disconnected', lambda ev: print(">>> component disconnected"))
|
||||
comp.add_event_handler('connection_failed', lambda ev: print(">>> connection_failed", ev))
|
||||
|
||||
# В slixmpp 1.17 ComponentXMPP НЕ обрабатывает входящие IQ (только handshake + presence_probe) —
|
||||
# регистрируем свой Callback на IQ, чтобы поймать ответ mod_privilege
|
||||
def _on_iq_cb(iq):
|
||||
on_iq(iq)
|
||||
|
||||
comp.register_handler(
|
||||
Callback('IQPriv', MatchXPath('{jabber:component:accept}iq'), _on_iq_cb))
|
||||
|
||||
try:
|
||||
await asyncio.wait_for(comp.connect('127.0.0.1', 15347), 15)
|
||||
await asyncio.wait_for(comp.wait_until('disconnected'), 30)
|
||||
except asyncio.TimeoutError:
|
||||
print(">>> TIMEOUT (нет ответа от mod_privilege)")
|
||||
except Exception as e:
|
||||
print(f">>> EXC: {type(e).__name__}: {e}")
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,85 @@
|
||||
-- Тестовый конфиг Prosody 13.0 (параллельный стенд для проверки mod_privilege)
|
||||
-- Домен: test.nixg.ru · Порты: 15222 (c2s) / 15269 (s2s) / 15280 (http)
|
||||
-- Запущен отдельно от прода (icq-prosody на 5222/5269/5280)
|
||||
|
||||
data_path = "/var/lib/prosody"
|
||||
plugin_paths = { "/etc/prosody/modules" }
|
||||
|
||||
log = {
|
||||
debug = "/var/log/prosody/prosody.log";
|
||||
error = "/var/log/prosody/prosody.err";
|
||||
}
|
||||
|
||||
admins = { "admin@test.nixg.ru" }
|
||||
|
||||
modules_enabled = {
|
||||
"roster"; -- Список контактов
|
||||
"saslauth"; -- Аутентификация
|
||||
"tls"; -- TLS
|
||||
"dialback"; -- s2s dialback
|
||||
"disco"; -- Service discovery
|
||||
"carbons"; -- Копии сообщений на все устройства
|
||||
"pep"; -- Personal eventing (OMEMO, avatars)
|
||||
"admin_shell"; -- временно: prosodyctl shell для диагностики
|
||||
"private"; -- Приватные хранилища XML
|
||||
"blocklist"; -- Чёрный список
|
||||
"vcard4"; -- vCard 4
|
||||
"vcard_legacy"; -- vCard 2
|
||||
"version"; -- Версия сервера
|
||||
"uptime"; -- Время работы
|
||||
"time"; -- Время
|
||||
"ping"; -- XMPP Ping
|
||||
"register"; -- In-band registration (для теста можно включить)
|
||||
"adhoc"; -- ad-hoc команды
|
||||
"admin_adhoc"; -- админ-команды
|
||||
"bosh"; -- BOSH
|
||||
"csi"; -- Client State Indication
|
||||
"offline"; -- Хранение оффлайн-сообщений
|
||||
"presence"; -- Присутствие
|
||||
"mam"; -- Message Archive Management
|
||||
"websocket"; -- WebSocket
|
||||
"server_contact_info";
|
||||
}
|
||||
|
||||
-- Порты (локальные, чтобы не конфликтовать с продом icq-prosody)
|
||||
c2s_ports = { 15222 }
|
||||
s2s_ports = { 15269 }
|
||||
c2s_interface = "0.0.0.0"
|
||||
s2s_interface = "0.0.0.0"
|
||||
|
||||
http_ports = { 15280 }
|
||||
http_interfaces = { "0.0.0.0" }
|
||||
|
||||
-- Компоненты (XEP-0114) — в 13.0 сервис 'component' слушает только default 5347
|
||||
-- (опция component_ports удалена из кода 13.0). Проброс: хост 15347 -> контейнер 5347.
|
||||
-- ВАЖНО: (1) внешний компонент объявляется блоком Component БЕЗ модуля, иначе
|
||||
-- mod_component не загрузится и порт 5347 не будет слушаться;
|
||||
-- (2) component_interfaces обязана быть в ГЛОБАЛЬНОЙ секции (выше VirtualHost/Component),
|
||||
-- иначе Prosody молча игнорирует её и listener висит на 127.0.0.1.
|
||||
component_interfaces = { "0.0.0.0" }
|
||||
|
||||
Component "telegram.test.nixg.ru"
|
||||
component_secret = "test-secret-telegram-123"
|
||||
modules_enabled = { "privilege" } -- чтобы mod_privilege реагировал на component-authenticated
|
||||
|
||||
-- Тестовый домен
|
||||
VirtualHost "test.nixg.ru"
|
||||
authentication = "internal_hashed"
|
||||
storage = "internal"
|
||||
allow_registration = true -- для теста можно регистрироваться in-band
|
||||
modules_enabled = { "privilege" } -- XEP-0356: модуль должен жить на VirtualHost, чтобы видеть privileged_entities
|
||||
-- Привилегии для проверки mod_privilege (XEP-0356)
|
||||
privileged_entities = {
|
||||
["telegram.test.nixg.ru"] = {
|
||||
roster = "both";
|
||||
message = "outgoing";
|
||||
iq = {
|
||||
["http://jabber.org/protocol/pubsub"] = "both";
|
||||
["http://jabber.org/protocol/pubsub#owner"] = "set";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
-- PubSub как компонент (XEP-0060; в 13.0 модуль pubsub в VirtualHost не грузится)
|
||||
Component "pubsub.test.nixg.ru" "pubsub"
|
||||
modules_enabled = { "pubsub" }
|
||||
Reference in New Issue
Block a user