Public Access
All seven screens of the design, plus the two it does not draw because it starts at an enrolled phone: naming a server, and choosing a passphrase. The five states docs/android-port.md worried about losing at 360dp are all here and none of them softened. The changed-key refusal is a full-screen panel rather than a bottom sheet, because a sheet is swipe-to-dismiss by convention and that screen must have no way forward. The recovery code raises FLAG_SECURE for its own state and lowers it afterwards, so the sentence about screenshots is true rather than decorative. The delete confirmations keep their counts and replace the row in place. Signing in works, and the seam it needed is worth more than the implementation: IAuthorizationCallback now sits between OidcClient and the loopback listener, so the two heads differ in where the response arrives and in nothing else. PKCE, the state check, discovery, the token exchange and the key binding stay one implementation — a second OIDC client would be a second place for a security bug to live. The phone registers a private-use scheme with the system rather than binding a loopback port, which on a shared device any other app can do first. The accessory key row needed TerminalWorkspace.SendInputAsync: ordinary typing goes from the renderer straight down the socket, and there was no way in for the keys a software keyboard does not have. Ctrl latches, because one thumb cannot chord, and the latch is drawn — a modifier that is on and does not look on is how somebody sends ^L to a database prompt believing they typed an l. 597 client tests green, including two new ones for the input path and one for the terminal surface command. Nothing has run on a device.
117 lines
5.7 KiB
XML
117 lines
5.7 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.Android.Views.HostKeySheet"
|
|
x:DataType="vm:VaultViewModel">
|
|
|
|
<!--
|
|
Designs 05 and 06 in one control, because they are one decision point with two answers and the shell
|
|
should not be able to show both. Which one draws is HasPendingHostKey against HasHostKeyMismatch, and
|
|
the view models make those mutually exclusive.
|
|
|
|
◆ Both are load-bearing, and the difference between them is the most important thing on either screen:
|
|
|
|
05 offers TRUST AND CONNECT, because first contact is a decision a person is entitled to make.
|
|
06 offers no way forward at all. There is no continue button, no "connect anyway", and no gesture that
|
|
dismisses it into a connection — the only exits are BACK and the host's own editor. That is deliberate
|
|
and the plan names presenting it as dismissible as the one design mistake that matters here. A phone
|
|
makes this easy to get wrong, because a bottom sheet is swipe-to-dismiss by convention; this is not a
|
|
sheet for that reason, it is a full-screen panel.
|
|
-->
|
|
|
|
<Panel>
|
|
|
|
<!-- ============ 05 UNKNOWN HOST KEY ============ -->
|
|
<Panel IsVisible="{Binding HasPendingHostKey}">
|
|
|
|
<!-- Scrim. Non-interactive by itself: tapping outside must not answer the question. -->
|
|
<Border Background="#9E040505" />
|
|
|
|
<Border VerticalAlignment="Bottom" Background="{StaticResource Panel}"
|
|
BorderBrush="{StaticResource BorderMid}" BorderThickness="0,1,0,0"
|
|
CornerRadius="22,22,0,0" Padding="20,18,20,16">
|
|
<ScrollViewer MaxHeight="620">
|
|
<StackPanel Spacing="0">
|
|
|
|
<Border Width="38" Height="4" CornerRadius="2" Background="{StaticResource BorderMid}"
|
|
HorizontalAlignment="Center" Margin="0,0,0,16" />
|
|
|
|
<TextBlock Classes="title" Text="UNKNOWN HOST KEY" FontSize="13" />
|
|
|
|
<TextBlock Classes="body" Margin="0,6,0,0">
|
|
<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" Margin="0,14,0,5" Foreground="{StaticResource TextFaint}"
|
|
Text="{Binding PendingHostKey.Algorithm}" />
|
|
|
|
<!-- ◆ In full, and wrapping rather than clipping. See the .fingerprint style. -->
|
|
<Border Background="{StaticResource Field}" BorderBrush="{StaticResource BorderMid}"
|
|
BorderThickness="1" CornerRadius="6" Padding="12,11">
|
|
<SelectableTextBlock Classes="fingerprint" Text="{Binding PendingHostKey.Fingerprint}" />
|
|
</Border>
|
|
|
|
<TextBlock Classes="detail" Margin="0,8,0,0" TextWrapping="Wrap"
|
|
Text="Compare with the fingerprint the operator published before trusting." />
|
|
|
|
<Button Classes="primary" Content="TRUST AND CONNECT" Margin="0,16,0,0"
|
|
Command="{Binding TrustHostKeyCommand}" />
|
|
|
|
<Button Classes="secondary" Content="CANCEL" Margin="0,8,0,0"
|
|
Command="{Binding RejectHostKeyCommand}" />
|
|
|
|
</StackPanel>
|
|
</ScrollViewer>
|
|
</Border>
|
|
</Panel>
|
|
|
|
<!-- ============ 06 CHANGED HOST KEY ============ -->
|
|
<!--
|
|
Opaque and full-screen, not a sheet over the previous screen. There is nothing behind this worth
|
|
seeing and nothing behind it worth tapping.
|
|
-->
|
|
<Border IsVisible="{Binding HasHostKeyMismatch}" Background="{StaticResource Canvas}">
|
|
<ScrollViewer>
|
|
<StackPanel Margin="22,40,22,24" Spacing="0">
|
|
|
|
<Border Width="44" Height="44" BorderBrush="{StaticResource Danger}" BorderThickness="1.5"
|
|
CornerRadius="6" HorizontalAlignment="Left">
|
|
<TextBlock Text="!" Foreground="{StaticResource Danger}" FontFamily="{StaticResource MonoFont}"
|
|
FontSize="20" FontWeight="SemiBold"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
|
</Border>
|
|
|
|
<TextBlock Classes="title" Text="HOST KEY CHANGED" FontSize="17"
|
|
Foreground="{StaticResource Danger}" Margin="0,16,0,0" />
|
|
|
|
<!--
|
|
The whole explanation comes from the view model, which already composes it for the desktop
|
|
head — including which host, which dates, and both fingerprints. Rewriting it here would be a
|
|
second copy of the most safety-critical sentence in the product.
|
|
-->
|
|
<SelectableTextBlock Classes="fingerprint" Margin="0,12,0,0" FontSize="11"
|
|
Foreground="{StaticResource Text}"
|
|
Text="{Binding HostKeyMismatch}" />
|
|
|
|
<TextBlock Classes="body" Margin="0,16,0,0" Foreground="{StaticResource TextFaint}"
|
|
Text="If this server was rebuilt on purpose, open the host's editor and use FORGET HOST KEY, then connect again. There is no way to continue from this screen — that is deliberate." />
|
|
|
|
<!--
|
|
The only control on the screen, and it goes back rather than forward. Compare the sheet above,
|
|
where the accent button connects.
|
|
-->
|
|
<Button Classes="secondary" Content="BACK TO HOSTS" Margin="0,24,0,0"
|
|
Command="{Binding RejectHostKeyCommand}" />
|
|
|
|
</StackPanel>
|
|
</ScrollViewer>
|
|
</Border>
|
|
|
|
</Panel>
|
|
|
|
</UserControl>
|