Skip to content

Commit a8ab92f

Browse files
committed
Release prep v1.0.0.9: splash/video/versioning and MSI pipeline updates
1 parent 5c49a30 commit a8ab92f

14 files changed

Lines changed: 569 additions & 114 deletions

Directory.Build.props

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project>
2+
<PropertyGroup>
3+
<Version>1.0.0.9</Version>
4+
<FileVersion>1.0.0.9</FileVersion>
5+
<AssemblyVersion>1.0.0.9</AssemblyVersion>
6+
<InformationalVersion>1.0.0.9</InformationalVersion>
7+
</PropertyGroup>
8+
</Project>

SecKey.App/App.xaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,45 @@
7272
<Setter Property="BorderBrush" Value="{DynamicResource StatusPanelBorderBrush}"/>
7373
</Style>
7474

75+
<Style TargetType="TabControl">
76+
<Setter Property="Foreground" Value="{DynamicResource WindowForegroundBrush}"/>
77+
<Setter Property="Background" Value="Transparent"/>
78+
<Setter Property="BorderBrush" Value="{DynamicResource StatusPanelBorderBrush}"/>
79+
<Setter Property="Padding" Value="0"/>
80+
</Style>
81+
82+
<Style TargetType="TabItem">
83+
<Setter Property="Margin" Value="0,0,6,0"/>
84+
<Setter Property="Padding" Value="14,7"/>
85+
<Setter Property="Foreground" Value="{DynamicResource WindowForegroundBrush}"/>
86+
<Setter Property="Background" Value="{DynamicResource CardBrush}"/>
87+
<Setter Property="BorderBrush" Value="{DynamicResource StatusPanelBorderBrush}"/>
88+
<Setter Property="BorderThickness" Value="1"/>
89+
<Setter Property="Template">
90+
<Setter.Value>
91+
<ControlTemplate TargetType="TabItem">
92+
<Border x:Name="TabBorder"
93+
Background="{TemplateBinding Background}"
94+
BorderBrush="{TemplateBinding BorderBrush}"
95+
BorderThickness="{TemplateBinding BorderThickness}"
96+
CornerRadius="10,10,0,0"
97+
Padding="{TemplateBinding Padding}">
98+
<ContentPresenter ContentSource="Header" HorizontalAlignment="Center" VerticalAlignment="Center"/>
99+
</Border>
100+
<ControlTemplate.Triggers>
101+
<Trigger Property="IsSelected" Value="True">
102+
<Setter TargetName="TabBorder" Property="Background" Value="{DynamicResource StatusPanelBrush}"/>
103+
<Setter TargetName="TabBorder" Property="BorderBrush" Value="#5EA1FF"/>
104+
</Trigger>
105+
<Trigger Property="IsEnabled" Value="False">
106+
<Setter Property="Opacity" Value="0.6"/>
107+
</Trigger>
108+
</ControlTemplate.Triggers>
109+
</ControlTemplate>
110+
</Setter.Value>
111+
</Setter>
112+
</Style>
113+
75114
<Style TargetType="DataGrid">
76115
<Setter Property="BorderBrush" Value="{DynamicResource StatusPanelBorderBrush}"/>
77116
<Setter Property="BorderThickness" Value="1"/>

SecKey.App/App.xaml.cs

Lines changed: 129 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
using System.Windows;
1+
using System.Diagnostics;
2+
using System.Windows;
23
using Microsoft.Extensions.DependencyInjection;
34
using Microsoft.Extensions.Logging;
45
using SecKey.App.Services;
56
using SecKey.App.ViewModels;
7+
using SecKey.App.Views;
68
using SecKey.Core.Services;
79
using SecKey.Graph;
810
using SecKey.Graph.Auth;
@@ -13,6 +15,8 @@ public partial class App : Application
1315
{
1416
public IServiceProvider Services { get; private set; } = null!;
1517
public static new App Current => (App)Application.Current;
18+
private SplashScreenWindow? _splashScreen;
19+
private readonly Stopwatch _startupStopwatch = Stopwatch.StartNew();
1620

1721
private static void WriteCrashLog(string source, Exception? ex)
1822
{
@@ -51,84 +55,137 @@ protected override void OnStartup(StartupEventArgs e)
5155
a.SetObserved();
5256
};
5357

54-
try
58+
_splashScreen = new SplashScreenWindow();
59+
MainWindow = _splashScreen;
60+
_splashScreen.Show();
61+
_splashScreen.Dispatcher.Invoke(() => { }, System.Windows.Threading.DispatcherPriority.Render);
62+
63+
Dispatcher.BeginInvoke(new Action(async () =>
5564
{
56-
var services = new ServiceCollection();
57-
services.AddLogging(b => b.AddDebug().SetMinimumLevel(LogLevel.Information));
65+
try
66+
{
67+
await InitializeApplicationAsync();
68+
}
69+
catch (Exception ex)
70+
{
71+
WriteCrashLog("Startup", ex);
72+
MessageBox.Show(
73+
$"SecKey failed to start. Details were logged to %LocalAppData%\\SecKey\\crash.log.\n\n{ex}",
74+
"Startup Error",
75+
MessageBoxButton.OK,
76+
MessageBoxImage.Error);
77+
Shutdown(-1);
78+
}
79+
}));
80+
}
5881

59-
// Default auth options; the Login page replaces the registered ITokenProvider with a configured one.
60-
var defaultOptions = new AuthOptions
82+
private async Task InitializeApplicationAsync()
83+
{
84+
try
6185
{
62-
Mode = AuthMode.Interactive,
63-
TenantId = "common",
64-
Scopes = new[]
86+
_splashScreen?.UpdateStatus("Preparing app services...");
87+
88+
var services = await Task.Run(() =>
6589
{
66-
"https://graph.microsoft.com/Directory.ReadWrite.All",
67-
"https://graph.microsoft.com/DeviceManagementApps.ReadWrite.All",
68-
"https://graph.microsoft.com/DeviceManagementConfiguration.ReadWrite.All",
69-
"https://graph.microsoft.com/DeviceManagementScripts.ReadWrite.All",
70-
"https://graph.microsoft.com/DeviceManagementServiceConfig.ReadWrite.All",
71-
"https://graph.microsoft.com/Group.ReadWrite.All",
72-
"https://graph.microsoft.com/User.ReadWrite.All",
73-
"https://graph.microsoft.com/Policy.ReadWrite.ConditionalAccess"
90+
var collection = new ServiceCollection();
91+
collection.AddLogging(b => b.AddDebug().SetMinimumLevel(LogLevel.Information));
92+
93+
// Default auth options; the Login page replaces the registered ITokenProvider with a configured one.
94+
var defaultOptions = new AuthOptions
95+
{
96+
Mode = AuthMode.Interactive,
97+
TenantId = "common",
98+
Scopes = new[]
99+
{
100+
"https://graph.microsoft.com/Directory.ReadWrite.All",
101+
"https://graph.microsoft.com/DeviceManagementApps.ReadWrite.All",
102+
"https://graph.microsoft.com/DeviceManagementConfiguration.ReadWrite.All",
103+
"https://graph.microsoft.com/DeviceManagementScripts.ReadWrite.All",
104+
"https://graph.microsoft.com/DeviceManagementServiceConfig.ReadWrite.All",
105+
"https://graph.microsoft.com/Group.ReadWrite.All",
106+
"https://graph.microsoft.com/User.ReadWrite.All",
107+
"https://graph.microsoft.com/Policy.ReadWrite.ConditionalAccess"
108+
}
109+
};
110+
111+
collection.AddSecKeyAuth(defaultOptions);
112+
collection.AddSecKeyGraph();
113+
collection.AddTransient<ISystemAuditService, SystemAuditService>();
114+
collection.AddSingleton<INativeDeploymentSettingsService, NativeDeploymentSettingsService>();
115+
collection.AddSingleton<AppSettingsExchangeService>();
116+
collection.AddSingleton<AppUpdateService>();
117+
collection.AddSingleton<BaselineContentBootstrapService>();
118+
119+
collection.AddSingleton<EntraConfigService>(_ => EntraConfigService.Instance);
120+
collection.AddSingleton<AuthState>();
121+
collection.AddSingleton<MainViewModel>();
122+
collection.AddTransient<LoginViewModel>();
123+
collection.AddTransient<DashboardViewModel>();
124+
collection.AddTransient<IntuneAppsViewModel>();
125+
collection.AddTransient<InfrastructureViewModel>();
126+
collection.AddTransient<PoliciesViewModel>();
127+
collection.AddTransient<GroupsViewModel>();
128+
collection.AddTransient<ConditionalAccessViewModel>();
129+
collection.AddTransient<UploadAppViewModel>();
130+
collection.AddTransient<DeviceTaggingViewModel>();
131+
collection.AddTransient<SecurityAnalyzerViewModel>();
132+
collection.AddTransient<SystemHardeningViewModel>();
133+
collection.AddTransient<RebootAnalyzerViewModel>();
134+
collection.AddTransient<FileIntegrityViewModel>();
135+
collection.AddTransient<IntuneBackupViewModel>();
136+
collection.AddTransient<CertificateManagerViewModel>();
137+
collection.AddTransient<SecureWipeViewModel>();
138+
collection.AddTransient<NetworkTrafficViewModel>();
139+
collection.AddTransient<HashScannerViewModel>();
140+
collection.AddTransient<CredentialManagerViewModel>();
141+
collection.AddTransient<EncryptedClipboardViewModel>();
142+
collection.AddTransient<SshKeyManagerViewModel>();
143+
collection.AddTransient<FileEncryptionToolViewModel>();
144+
collection.AddTransient<SecurityVaultViewModel>();
145+
collection.AddTransient<YaraScannerViewModel>();
146+
collection.AddTransient<CveSearchViewModel>();
147+
collection.AddTransient<ForensicsAnalyzerViewModel>();
148+
collection.AddTransient<AdvancedForensicsViewModel>();
149+
collection.AddTransient<GlobalSecureAccessViewModel>();
150+
collection.AddTransient<WdacAppLockerViewModel>();
151+
collection.AddTransient<SystemAuditViewModel>();
152+
collection.AddTransient<PreferencesViewModel>();
153+
154+
return collection.BuildServiceProvider();
155+
});
156+
157+
Services = services;
158+
159+
_splashScreen?.UpdateStatus("Seeding baseline content...");
160+
try
161+
{
162+
await Task.Run(() => Services.GetRequiredService<BaselineContentBootstrapService>().EnsureBaselineContent());
74163
}
75-
};
76-
services.AddSecKeyAuth(defaultOptions);
77-
services.AddSecKeyGraph();
78-
services.AddTransient<ISystemAuditService, SystemAuditService>();
79-
services.AddSingleton<INativeDeploymentSettingsService, NativeDeploymentSettingsService>();
80-
services.AddSingleton<AppSettingsExchangeService>();
81-
services.AddSingleton<AppUpdateService>();
82-
83-
services.AddSingleton<EntraConfigService>(_ => EntraConfigService.Instance);
84-
services.AddSingleton<AuthState>();
85-
services.AddSingleton<MainViewModel>();
86-
services.AddTransient<LoginViewModel>();
87-
services.AddTransient<DashboardViewModel>();
88-
services.AddTransient<IntuneAppsViewModel>();
89-
services.AddTransient<InfrastructureViewModel>();
90-
services.AddTransient<PoliciesViewModel>();
91-
services.AddTransient<GroupsViewModel>();
92-
services.AddTransient<ConditionalAccessViewModel>();
93-
services.AddTransient<UploadAppViewModel>();
94-
services.AddTransient<DeviceTaggingViewModel>();
95-
services.AddTransient<SecurityAnalyzerViewModel>();
96-
services.AddTransient<SystemHardeningViewModel>();
97-
services.AddTransient<RebootAnalyzerViewModel>();
98-
services.AddTransient<FileIntegrityViewModel>();
99-
services.AddTransient<IntuneBackupViewModel>();
100-
services.AddTransient<CertificateManagerViewModel>();
101-
services.AddTransient<SecureWipeViewModel>();
102-
services.AddTransient<NetworkTrafficViewModel>();
103-
services.AddTransient<HashScannerViewModel>();
104-
services.AddTransient<CredentialManagerViewModel>();
105-
services.AddTransient<EncryptedClipboardViewModel>();
106-
services.AddTransient<SshKeyManagerViewModel>();
107-
services.AddTransient<FileEncryptionToolViewModel>();
108-
services.AddTransient<SecurityVaultViewModel>();
109-
services.AddTransient<YaraScannerViewModel>();
110-
services.AddTransient<CveSearchViewModel>();
111-
services.AddTransient<ForensicsAnalyzerViewModel>();
112-
services.AddTransient<AdvancedForensicsViewModel>();
113-
services.AddTransient<GlobalSecureAccessViewModel>();
114-
services.AddTransient<WdacAppLockerViewModel>();
115-
services.AddTransient<SystemAuditViewModel>();
116-
services.AddTransient<PreferencesViewModel>();
117-
118-
Services = services.BuildServiceProvider();
119-
120-
var window = new MainWindow { DataContext = Services.GetRequiredService<MainViewModel>() };
121-
window.Show();
164+
catch (Exception bootstrapEx)
165+
{
166+
WriteCrashLog("BaselineContentBootstrap", bootstrapEx);
167+
}
168+
169+
_splashScreen?.UpdateStatus("Loading workspace...");
170+
var minimumSplashTime = TimeSpan.FromSeconds(6);
171+
var remainingSplashTime = minimumSplashTime - _startupStopwatch.Elapsed;
172+
if (remainingSplashTime > TimeSpan.Zero)
173+
await Task.Delay(remainingSplashTime);
174+
175+
await Dispatcher.InvokeAsync(() =>
176+
{
177+
var window = new MainWindow { DataContext = Services.GetRequiredService<MainViewModel>() };
178+
MainWindow = window;
179+
window.Show();
180+
_splashScreen?.Close();
181+
_splashScreen = null;
182+
});
122183
}
123-
catch (Exception ex)
184+
catch
124185
{
125-
WriteCrashLog("Startup", ex);
126-
MessageBox.Show(
127-
$"SecKey failed to start. Details were logged to %LocalAppData%\\SecKey\\crash.log.\n\n{ex}",
128-
"Startup Error",
129-
MessageBoxButton.OK,
130-
MessageBoxImage.Error);
131-
Shutdown(-1);
186+
_splashScreen?.Close();
187+
_splashScreen = null;
188+
throw;
132189
}
133190
}
134191
}
13 MB
Binary file not shown.

SecKey.App/SecKey.App.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616
<Resource Include="Assets\PlatypusToolsLogo.png" />
1717
</ItemGroup>
1818

19+
<ItemGroup>
20+
<Content Include="Assets\PlatypusToolsIntro.mp4">
21+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
22+
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
23+
</Content>
24+
</ItemGroup>
25+
1926
<ItemGroup>
2027
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
2128
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />

SecKey.App/Services/AppUpdateService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,14 @@ private static string CreateZipUpdaterScript(string dir)
201201

202202
private static Version GetCurrentVersion()
203203
{
204+
var executablePath = Environment.ProcessPath ?? Assembly.GetEntryAssembly()?.Location;
205+
if (!string.IsNullOrWhiteSpace(executablePath) && File.Exists(executablePath))
206+
{
207+
var fileVersion = FileVersionInfo.GetVersionInfo(executablePath).FileVersion;
208+
if (Version.TryParse(fileVersion, out var parsedVersion))
209+
return parsedVersion;
210+
}
211+
204212
var entry = Assembly.GetEntryAssembly();
205213
if (entry?.GetName().Version is { } v)
206214
return v;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System.IO;
2+
3+
namespace SecKey.App.Services;
4+
5+
public sealed class BaselineContentBootstrapService
6+
{
7+
private static readonly string[] ContentFolders = ["JSON", "IntuneApps", "RemediationScripts"];
8+
9+
private readonly string _cacheRoot;
10+
11+
public BaselineContentBootstrapService()
12+
{
13+
_cacheRoot = Path.Combine(
14+
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
15+
"SecKey",
16+
"BaselineContent");
17+
}
18+
19+
public void EnsureBaselineContent()
20+
{
21+
Directory.CreateDirectory(_cacheRoot);
22+
23+
var appBase = AppContext.BaseDirectory;
24+
foreach (var folder in ContentFolders)
25+
{
26+
var appFolder = Path.Combine(appBase, folder);
27+
var cacheFolder = Path.Combine(_cacheRoot, folder);
28+
29+
var appExists = Directory.Exists(appFolder);
30+
var cacheExists = Directory.Exists(cacheFolder);
31+
32+
if (appExists && !cacheExists)
33+
{
34+
CopyDirectory(appFolder, cacheFolder);
35+
}
36+
else if (!appExists && cacheExists)
37+
{
38+
CopyDirectory(cacheFolder, appFolder);
39+
}
40+
}
41+
}
42+
43+
private static void CopyDirectory(string sourceDirectory, string destinationDirectory)
44+
{
45+
if (!Directory.Exists(sourceDirectory))
46+
return;
47+
48+
Directory.CreateDirectory(destinationDirectory);
49+
50+
foreach (var sourceFile in Directory.EnumerateFiles(sourceDirectory, "*", SearchOption.AllDirectories))
51+
{
52+
var relativePath = Path.GetRelativePath(sourceDirectory, sourceFile);
53+
var destinationFile = Path.Combine(destinationDirectory, relativePath);
54+
var destinationPath = Path.GetDirectoryName(destinationFile);
55+
if (!string.IsNullOrWhiteSpace(destinationPath))
56+
Directory.CreateDirectory(destinationPath);
57+
58+
File.Copy(sourceFile, destinationFile, overwrite: true);
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)