Files
DiscordChatExporter/DiscordChatExporter.Gui/Services/SettingsService.cs
Copilot eef0fc742d Encrypt Discord token at rest in settings file (machine-bound) (#1491)
* Initial plan

* Add token encryption when saving/loading settings

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Apply suggestion from @Tyrrrz

* Apply suggestion from @Tyrrrz

* Bind token encryption key to machine identity

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Switch to AES-GCM, hex encoding, and GetBytes/Fill improvements

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Address all review feedback: salt injection, code style, localization formatting

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Address latest review: ThisAssembly.Project, EnvironmentExtensions, inline Lazy, renames, localization wording

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Address latest review: layout comment, cipherSource, else block, MachineName fallback, csproj ordering

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Apply suggestion from @Tyrrrz

* Rename GetMachineId→TryGetMachineId, refactor Write to use single array with FillBytes

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Rename cipherSource→cipher in Read(), tokenBytes→tokenData in Write(), update layout comments

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Add cipherSource variable in Write(), update layout comment with size annotation

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Fix CSharpier formatting: inline multiline string assignments and reformat exception filter

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Quote EncryptionSalt argument to handle single quotes in secret value

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Revert double-quote fix on EncryptionSalt argument

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Apply suggestion from @Tyrrrz

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-27 14:01:25 +02:00

98 lines
2.7 KiB
C#

using System;
using System.IO;
using System.Text.Json.Serialization;
using Cogwheel;
using CommunityToolkit.Mvvm.ComponentModel;
using DiscordChatExporter.Core.Discord;
using DiscordChatExporter.Core.Exporting;
using DiscordChatExporter.Gui.Framework;
using DiscordChatExporter.Gui.Localization;
using DiscordChatExporter.Gui.Models;
namespace DiscordChatExporter.Gui.Services;
[ObservableObject]
public partial class SettingsService()
: SettingsBase(
Path.Combine(AppContext.BaseDirectory, "Settings.dat"),
SerializerContext.Default
)
{
[ObservableProperty]
public partial bool IsUkraineSupportMessageEnabled { get; set; } = true;
[ObservableProperty]
public partial ThemeVariant Theme { get; set; }
[ObservableProperty]
public partial Language Language { get; set; }
[ObservableProperty]
public partial bool IsAutoUpdateEnabled { get; set; } = true;
[ObservableProperty]
public partial bool IsTokenPersisted { get; set; } = true;
[ObservableProperty]
public partial RateLimitPreference RateLimitPreference { get; set; } =
RateLimitPreference.RespectAll;
[ObservableProperty]
public partial ThreadInclusionMode ThreadInclusionMode { get; set; }
[ObservableProperty]
public partial string? Locale { get; set; }
[ObservableProperty]
public partial bool IsUtcNormalizationEnabled { get; set; }
[ObservableProperty]
public partial int ParallelLimit { get; set; } = 1;
[ObservableProperty]
[JsonConverter(typeof(TokenEncryptionConverter))]
public partial string? LastToken { get; set; }
[ObservableProperty]
public partial ExportFormat LastExportFormat { get; set; } = ExportFormat.HtmlDark;
[ObservableProperty]
public partial string? LastPartitionLimitValue { get; set; }
[ObservableProperty]
public partial string? LastMessageFilterValue { get; set; }
[ObservableProperty]
public partial bool LastIsReverseMessageOrder { get; set; }
[ObservableProperty]
public partial bool LastShouldFormatMarkdown { get; set; } = true;
[ObservableProperty]
public partial bool LastShouldDownloadAssets { get; set; }
[ObservableProperty]
public partial bool LastShouldReuseAssets { get; set; }
[ObservableProperty]
public partial string? LastAssetsDirPath { get; set; }
public override void Save()
{
// Clear the token if it's not supposed to be persisted
var lastToken = LastToken;
if (!IsTokenPersisted)
LastToken = null;
base.Save();
LastToken = lastToken;
}
}
public partial class SettingsService
{
[JsonSerializable(typeof(SettingsService))]
private partial class SerializerContext : JsonSerializerContext;
}