Add RDP user consent popup before screen sharing

When rdp_start arrives, agent first spawns a WPF consent dialog in the
user session (via schtasks). User has 30s to accept or deny. On deny,
agent sends rdp_denied to browser which shows "Zugriff abgelehnt".
On accept, screen capture starts as before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 10:19:43 +02:00
parent 058f968af1
commit d42dc562a3
6 changed files with 328 additions and 2 deletions

View File

@@ -0,0 +1,142 @@
<Window x:Class="ITNexusAgent.UI.RdpConsentWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IT Nexus Bildschirmzugriff"
Width="480" Height="Auto"
SizeToContent="Height"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
WindowStyle="None"
AllowsTransparency="False"
Background="#1A1D2E"
Topmost="True"
FontFamily="Segoe UI">
<Window.Resources>
<Style x:Key="AcceptBtn" TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Height" Value="46"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bg" CornerRadius="8" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bg" Property="Opacity" Value="0.85"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bg" Property="Opacity" Value="0.7"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DenyBtn" TargetType="Button">
<Setter Property="Foreground" Value="#B0B8D1"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="#3D4270"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="Height" Value="46"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bg" CornerRadius="8"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bg" Property="Opacity" Value="0.75"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Border Background="#1A1D2E" BorderBrush="#2D3250" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Orange Akzentlinie oben -->
<Border Grid.Row="0" Background="#E67E22" CornerRadius="12,12,0,0"/>
<!-- Header -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="24,20,24,16" VerticalAlignment="Center">
<Border Width="44" Height="44" CornerRadius="10"
Background="#3D2010" BorderBrush="#6B3A18" BorderThickness="1"
VerticalAlignment="Top" Margin="0,0,14,0">
<TextBlock Text="🖥️" FontSize="22" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="BILDSCHIRMZUGRIFF ANGEFORDERT" FontSize="10" FontWeight="Bold"
Foreground="#E67E22" TextOptions.TextFormattingMode="Display"/>
<TextBlock Text="IT-Abteilung möchte Ihren Bildschirm anzeigen" FontSize="16" FontWeight="Bold"
Foreground="White" TextWrapping="Wrap" MaxWidth="360" Margin="0,3,0,0"/>
</StackPanel>
</StackPanel>
<!-- Info-Box -->
<Border Grid.Row="2" Margin="24,0,24,16"
Background="#252840" CornerRadius="8"
BorderBrush="#3D4270" BorderThickness="1" Padding="14,12">
<StackPanel>
<TextBlock FontSize="13" Foreground="#B0B8D1" TextWrapping="Wrap" LineHeight="20">
Ein IT-Administrator möchte sich mit Ihrem Bildschirm verbinden.
Ihr Bildschirminhalt wird dabei übertragen und ist für die IT sichtbar.
</TextBlock>
<TextBlock Margin="0,8,0,0" FontSize="12" Foreground="#6B7590"
TextWrapping="Wrap">
Lehnen Sie ab, falls Sie diese Anfrage nicht erwartet haben oder
gerade sensible Daten auf dem Bildschirm haben.
</TextBlock>
</StackPanel>
</Border>
<!-- Countdown -->
<TextBlock Grid.Row="3" x:Name="CountdownText"
Margin="24,0,24,14" FontSize="11" Foreground="#555E7A"
HorizontalAlignment="Center"/>
<!-- Buttons -->
<Grid Grid.Row="4" Margin="24,0,24,24">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="DenyButton"
Style="{StaticResource DenyBtn}"
Background="#1E2030"
Click="DenyButton_Click">
<TextBlock Text="✗ Ablehnen" FontSize="13"/>
</Button>
<Button Grid.Column="2" x:Name="AcceptButton"
Style="{StaticResource AcceptBtn}"
Background="#238636"
Click="AcceptButton_Click">
<TextBlock Text="✓ Zulassen" FontSize="13" FontWeight="Bold"/>
</Button>
</Grid>
</Grid>
</Border>
</Window>

View File

@@ -0,0 +1,61 @@
using System.Net.Sockets;
using System.Windows;
namespace ITNexusAgent.UI;
public partial class RdpConsentWindow : Window
{
private readonly int _port;
private bool _answered = false;
private System.Threading.CancellationTokenSource _countdownCts = new();
public RdpConsentWindow(int port)
{
InitializeComponent();
_port = port;
_ = RunCountdownAsync(_countdownCts.Token);
}
private async Task RunCountdownAsync(System.Threading.CancellationToken ct)
{
for (int i = 30; i > 0; i--)
{
if (ct.IsCancellationRequested) return;
Dispatcher.Invoke(() => CountdownText.Text = $"Automatische Ablehnung in {i} Sekunden");
await Task.Delay(1000, ct).ContinueWith(_ => { });
}
if (!_answered)
{
SendResponse(false);
Dispatcher.Invoke(Close);
}
}
private void AcceptButton_Click(object sender, RoutedEventArgs e)
{
_countdownCts.Cancel();
SendResponse(true);
Close();
}
private void DenyButton_Click(object sender, RoutedEventArgs e)
{
_countdownCts.Cancel();
SendResponse(false);
Close();
}
private void SendResponse(bool accepted)
{
if (_answered) return;
_answered = true;
try
{
using var tcp = new TcpClient();
tcp.Connect("127.0.0.1", _port);
tcp.GetStream().WriteByte((byte)(accepted ? 1 : 0));
tcp.GetStream().Flush();
}
catch { }
}
}