feat: RDP flap auto-disconnect and hide break button after reconnect (0.4.10)

Add optional auto logoff of stuck sessions on RDG flap and direct RDP failure. Hide the RDS break button on old flap 302 when the user later reconnects or the workstation session is closed.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-02 16:50:03 +10:00
parent 10ae670962
commit 3765d4d476
15 changed files with 854 additions and 7 deletions
+59 -1
View File
@@ -210,6 +210,57 @@ def find_normal_rdg_end_after_success(db: Session, success_event: Event) -> Even
return None
def _users_and_ip_match(left: Event, right: Event) -> bool:
return _users_match(left, right) and _internal_ips_match_strict(left, right)
def find_later_rdg_success_after(
db: Session,
*,
anchor: Event,
after: datetime,
) -> Event | None:
"""Поздний 302 на том же шлюзе, user и client PC — пользователь снова зашёл через RDG."""
if anchor.type != RDG_SUCCESS_TYPE:
return None
after_at = _as_utc(after)
candidates = db.scalars(
select(Event)
.where(
Event.host_id == anchor.host_id,
Event.type == RDG_SUCCESS_TYPE,
Event.occurred_at > after_at,
Event.id != anchor.id,
)
.order_by(Event.occurred_at.asc())
).all()
for later in candidates:
if not _users_and_ip_match(later, anchor):
continue
return later
return None
def _flap_auto_disconnect_succeeded(flap_end: Event) -> bool:
details = flap_end.details if isinstance(flap_end.details, dict) else {}
block = details.get("rdp_flap_auto_disconnect")
if not isinstance(block, dict):
return False
return block.get("ok") is True
def _flap_workstation_session_closed(db: Session, flap_end: Event) -> bool:
from app.services.host_sessions import event_session_terminated
from app.services.rdg_workstation_session import find_workstation_login_for_rdg_end
login = find_workstation_login_for_rdg_end(db, flap_end)
if login is None:
return False
return event_session_terminated(login, db=db)
def resolve_rdg_qwinsta_enabled(db: Session | None, event: Event) -> bool:
"""Кнопка qwinsta/logoff только на 302, пока сессия может быть активна (или RDG flap)."""
if event.type in RDG_END_TYPES:
@@ -220,7 +271,14 @@ def resolve_rdg_qwinsta_enabled(db: Session | None, event: Event) -> bool:
return False
if db is None:
return True
if find_rdg_end_after_success(db, event) is not None:
flap_end = find_rdg_end_after_success(db, event)
if flap_end is not None:
if _flap_auto_disconnect_succeeded(flap_end):
return False
if _flap_workstation_session_closed(db, flap_end):
return False
if find_later_rdg_success_after(db, anchor=event, after=flap_end.occurred_at) is not None:
return False
return True
if find_normal_rdg_end_after_success(db, event) is not None:
return False