Public Access
136 lines
7.9 KiB
XML
136 lines
7.9 KiB
XML
<UserControl xmlns="https://github.com/avaloniaui"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
|
|
x:Class="DodoSSH.Client.App.Views.HostKeyCard"
|
|
x:DataType="vm:VaultViewModel">
|
|
|
|
<!--
|
|
The two host-key decisions, over whatever the window happens to be showing. The desktop counterpart of
|
|
the phone's HostKeySheet, and one control for both answers for the same reason: they are one decision
|
|
point, the view models make them mutually exclusive, and the shell must not be able to show both.
|
|
|
|
── IT WAS TWO BANNERS AT THE TOP OF THE HOSTS SCREEN. ───────────────────────────────────────────────
|
|
Which is why the shell used to navigate there before letting the vault raise the question: a connection
|
|
can be started from the palette on any screen, and a prompt behind the screen somebody is looking at is a
|
|
prompt nobody can answer. The navigation covered that, and paid for it everywhere else — it took the user
|
|
off the screen they were working on, and for the one connection that has no host at all it judged a
|
|
machine typed in by hand on the list it is deliberately not on. See MainWindowViewModel's own note on
|
|
OnVaultConnectionFailed.
|
|
|
|
Drawn over the surface instead, so nothing has to move and the palette's case needs no special one.
|
|
|
|
── THE OCCLUSION RULE, AND THIS IS THE CONTROL THAT MOST NEEDS IT. ──────────────────────────────────
|
|
A second connection can be made while a first one is open, so this card is routinely raised over a live
|
|
terminal — and that rectangle is a native child window which composites above everything Avalonia draws.
|
|
A card layered over it would be sliced at its left edge with TRUST AND CONNECT unreachable, which for the
|
|
most safety-critical question in the product is the worst place for that class of bug to land. The shell
|
|
collapses the WebView while this is up; see MainWindowViewModel.IsHostKeyDecisionShowing.
|
|
|
|
── ◆ WHAT IS DELIBERATELY DIFFERENT BETWEEN THE TWO HALVES. ─────────────────────────────────────────
|
|
First contact offers TRUST AND CONNECT, because deciding whether a fingerprint is the one the operator
|
|
published is a judgement a person is entitled to make and is the only moment they can make it.
|
|
|
|
A changed key offers no way forward at all: no continue, no "connect anyway", and nothing that turns
|
|
dismissing it into a connection. The only way past it is FORGET HOST KEY in the host's own editor, which
|
|
is a deliberate act performed somewhere else — a button next to this warning would be "continue anyway"
|
|
with two clicks instead of one. The phone's sheet carries the same note, and calls presenting this as
|
|
dismissible the one design mistake here that matters.
|
|
-->
|
|
|
|
<!--
|
|
A wash with no press handler, and the omission is the point: every other overlay in this window closes by
|
|
clicking away from it, and a question with two named answers must not be answerable by missing. The
|
|
palette's own backdrop is the control to compare with — see QuickConnect.
|
|
|
|
80% of Canvas, written out because a scrim is a brush with an alpha and the palette holds no alpha
|
|
variant of a surface; the pre-multiplied ones there are accent washes.
|
|
-->
|
|
<!--
|
|
Tab cycles inside the card rather than walking the page under the wash, which it used to: a question
|
|
that cannot be answered by clicking away should not be answerable by tabbing away either.
|
|
-->
|
|
<Border Background="#CC05050A" KeyboardNavigation.TabNavigation="Cycle">
|
|
<Panel>
|
|
|
|
<!-- ============ UNKNOWN HOST KEY ============ -->
|
|
<Border Classes="card" IsVisible="{Binding HasPendingHostKey}"
|
|
BorderBrush="{StaticResource WarnSoft}">
|
|
<StackPanel Spacing="10">
|
|
|
|
<TextBlock Classes="label" Text="UNKNOWN HOST KEY" Foreground="{StaticResource Warn}" />
|
|
|
|
<TextBlock Foreground="{StaticResource WarnText}" TextWrapping="Wrap">
|
|
<Run Text="First contact with" />
|
|
<Run Text="{Binding PendingHostKey.Host}" Foreground="{StaticResource Text}" />
|
|
<Run Text="·" />
|
|
<Run Text="{Binding PendingHostKey.Port}" />
|
|
<Run Text=". Nothing in this keychain has approved this key." />
|
|
</TextBlock>
|
|
|
|
<TextBlock Classes="label" FontSize="10" Text="{Binding PendingHostKey.Algorithm}" />
|
|
|
|
<!--
|
|
In full, wrapping rather than trimmed, and selectable. A fingerprint exists to be compared
|
|
character by character against one an operator published, and the two things that ruin that are
|
|
an ellipsis in the middle and a line you cannot copy.
|
|
-->
|
|
<Border Background="{StaticResource Field}" BorderBrush="{StaticResource BorderMid}"
|
|
BorderThickness="1" CornerRadius="4" Padding="10,8">
|
|
<SelectableTextBlock Classes="mono" FontSize="12" TextWrapping="Wrap"
|
|
Foreground="{StaticResource Warn}"
|
|
Text="{Binding PendingHostKey.Fingerprint}" />
|
|
</Border>
|
|
|
|
<TextBlock Classes="hint" FontSize="12"
|
|
Text="Compare it with the fingerprint the server's operator published before trusting it. Trusting pins it for this keychain, on every machine." />
|
|
|
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
|
<Button Classes="accent" Content="TRUST AND CONNECT"
|
|
Command="{Binding TrustHostKeyCommand}" />
|
|
<Button x:Name="CancelButton" Classes="ghost" Content="CANCEL" Command="{Binding RejectHostKeyCommand}" />
|
|
</StackPanel>
|
|
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- ============ CHANGED HOST KEY ============ -->
|
|
<Border Classes="card" IsVisible="{Binding HasHostKeyMismatch}"
|
|
BorderBrush="{StaticResource DangerSoft}">
|
|
<StackPanel Spacing="10">
|
|
|
|
<TextBlock Classes="label" Text="HOST KEY CHANGED" Foreground="{StaticResource Danger}" />
|
|
|
|
<TextBlock Foreground="{StaticResource Danger}" FontWeight="SemiBold" TextWrapping="Wrap"
|
|
Text="The host key changed and the connection was refused." />
|
|
|
|
<!--
|
|
The whole explanation comes from the view model, which already composes it — which host, both
|
|
fingerprints, and which of them this keychain has pinned. Rewriting it here would be a second
|
|
copy of the most safety-critical sentence in the product, in a second place to forget.
|
|
-->
|
|
<Border Background="{StaticResource Field}" BorderBrush="{StaticResource BorderMid}"
|
|
BorderThickness="1" CornerRadius="4" Padding="10,8">
|
|
<SelectableTextBlock Classes="mono" FontSize="12" TextWrapping="Wrap"
|
|
Foreground="{StaticResource DangerText}"
|
|
Text="{Binding HostKeyMismatch}" />
|
|
</Border>
|
|
|
|
<TextBlock Classes="hint" FontSize="12" Foreground="{StaticResource WarnText}"
|
|
Text="If this server was rebuilt on purpose, edit the host and choose FORGET HOST KEY, then connect again. There is deliberately no way to continue from here." />
|
|
|
|
<!--
|
|
One button, and it goes nowhere. It puts the refusal away so the host's editor can be reached; it
|
|
is not an answer, because this half of the control has none to give. Compare the accent button
|
|
above, which connects.
|
|
-->
|
|
<Button x:Name="CloseButton" Classes="ghost" Content="CLOSE" HorizontalAlignment="Left"
|
|
Command="{Binding RejectHostKeyCommand}" />
|
|
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
</Panel>
|
|
</Border>
|
|
|
|
</UserControl>
|