Service: isolate the profile in ProgramData, out of all normal profile paths

Local checkpoint - NOT for public release. Ed: the service profile must never be
reachable except through the Service menu.

Also fixes a real bug: the service runs as SYSTEM, whose per-user data folder is NOT the
interactive user's - so a profile saved in the user's profiles folder (or AppConfig, both
per-user) was invisible to the service. It would have idled, never streaming.

- New RemSound.Core.ServiceStore: the service profile + its settings (logging) live in a
  MACHINE-WIDE ProgramData\RemSound\service location - same absolute path for the user
  (config dialog) and SYSTEM (service). Moved ServiceProfileName/ServiceLoggingEnabled off
  AppConfig (per-user) onto this store.
- ServiceSendHost.FromConfig + RemSoundService now read ServiceStore; ConfigureServiceProfile
  saves there (and migrates + deletes any profile left in the old user-folder location).
- Because it's no longer in the user's profiles folder, it can't appear in the startup
  picker, File->Open, Recent profiles, or the password manager (all of which read the user
  ProfileStore); the reserved-title filter in ListProfileTitles stays as belt-and-braces.
- Password button renamed "Set service profile password".
- New self-test "Service profile isolation": store is under ProgramData, the reserved title
  is filtered from the listing, and it round-trips through the machine-wide store.

Gate 27/27.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-13 08:23:10 +01:00
co-authored by Claude Opus 4.8
parent f0b35b2b8c
commit 4f5265d8b0
7 changed files with 139 additions and 34 deletions
+13 -11
View File
@@ -2229,21 +2229,23 @@ public sealed class MainForm : Form
/// the service is running, restarts it so the edits take effect.</summary>
private void ConfigureServiceProfile()
{
if (profileStore is null) return;
var cfg = AppConfig.Load();
Profile current;
try { current = profileStore.Load(ServiceControl.ServiceProfileTitle) ?? Profile.NewBlank(); }
catch { current = Profile.NewBlank(); }
// The service profile lives in the machine-wide ServiceStore (ProgramData), NOT the user's
// profiles folder — so it's readable by the SYSTEM service and fully isolated from the picker,
// recents and password manager. Migrate a profile left in the old (user-folder) location by the
// earlier design so a user who configured it before doesn't lose their settings.
var current = ServiceStore.LoadProfile();
if (current is null && profileStore is not null)
try { current = profileStore.Load(ServiceControl.ServiceProfileTitle); } catch { /* none */ }
current ??= Profile.NewBlank();
using var dlg = new ServiceProfileDialog(current, cfg.ServiceLoggingEnabled);
using var dlg = new ServiceProfileDialog(current, ServiceStore.LoadLoggingEnabled());
if (dlg.ShowDialog(this) != DialogResult.OK) return;
try
{
profileStore.Save(dlg.Result);
var c = AppConfig.Load();
c.ServiceProfileName = ServiceControl.ServiceProfileTitle;
c.ServiceLoggingEnabled = dlg.ServiceLoggingEnabled;
c.Save();
ServiceStore.SaveProfile(dlg.Result);
ServiceStore.SaveLoggingEnabled(dlg.ServiceLoggingEnabled);
// Remove any stray copy the old design left in the user's profiles folder.
try { profileStore?.Delete(ServiceControl.ServiceProfileTitle); } catch { /* best-effort */ }
if (ServiceControl.Query() == ServiceState.Running)
{
ServiceControl.RunElevated(ServiceControl.StopVerb);