From bd499f44e72fc03a2d47a550e677509cf0d43c10 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:04:57 -0400 Subject: [PATCH 1/6] Add GPOHelper --- common/Helpers/GPOHelper.cs | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 common/Helpers/GPOHelper.cs diff --git a/common/Helpers/GPOHelper.cs b/common/Helpers/GPOHelper.cs new file mode 100644 index 0000000000..64e7fd05dc --- /dev/null +++ b/common/Helpers/GPOHelper.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Win32; + +namespace DevHome.Common.Helpers; + +public class GPOHelper +{ + private enum GpoRuleConfigured + { + WrongValue = -3, // The policy is set to an unrecognized value + Unavailable = -2, // Couldn't access registry + NotConfigured = -1, // Policy is not configured + Disabled = 0, // Policy is disabled + Enabled = 1, // Policy is enabled + } + + // Registry path where gpo policy values are stored + private const string PoliciesScopeMachine = "HKEY_LOCAL_MACHINE"; + private const string PoliciesPath = @"\SOFTWARE\Policies\Microsoft\Windows\DevHome"; + + // Registry value names + private const string PolicyConfigureEnabledDevHome = "ConfigureEnabledDevHome"; + + private static GpoRuleConfigured GetConfiguredValue(string registryValueName) + { + try + { + var rawValue = Registry.GetValue( + keyName: PoliciesScopeMachine + PoliciesPath, + valueName: registryValueName, + defaultValue: GpoRuleConfigured.NotConfigured); + + // Value will be null if the subkey specified by keyName does not exist. + if (rawValue == null) + { + return GpoRuleConfigured.NotConfigured; + } + else if (rawValue is not int) + { + return GpoRuleConfigured.WrongValue; + } + else + { + return (GpoRuleConfigured)rawValue; + } + } + catch (System.Security.SecurityException) + { + // The user does not have the permissions required to read from the registry key. + return GpoRuleConfigured.Unavailable; + } + catch (System.IO.IOException) + { + // The RegistryKey that contains the specified value has been marked for deletion. + return GpoRuleConfigured.Unavailable; + } + catch (System.ArgumentException) + { + // keyName does not begin with a valid registry root. + return GpoRuleConfigured.NotConfigured; + } + } + + private static bool EvaluateConfiguredValue(string registryValueName, GpoRuleConfigured defaultValue) + { + var configuredValue = GetConfiguredValue(registryValueName); + if (configuredValue < 0) + { + configuredValue = defaultValue; + } + + return configuredValue == GpoRuleConfigured.Enabled; + } + + public static bool GetConfiguredEnabledDevHomeValue() + { + var defaultValue = GpoRuleConfigured.Enabled; + return EvaluateConfiguredValue(PolicyConfigureEnabledDevHome, defaultValue); + } +} From 357ac57e46829ba3ee71071a7f3b58f637686a20 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:40:53 -0400 Subject: [PATCH 2/6] Change PoliciesPath --- common/Helpers/GPOHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/Helpers/GPOHelper.cs b/common/Helpers/GPOHelper.cs index 64e7fd05dc..2f89e6555d 100644 --- a/common/Helpers/GPOHelper.cs +++ b/common/Helpers/GPOHelper.cs @@ -18,7 +18,7 @@ private enum GpoRuleConfigured // Registry path where gpo policy values are stored private const string PoliciesScopeMachine = "HKEY_LOCAL_MACHINE"; - private const string PoliciesPath = @"\SOFTWARE\Policies\Microsoft\Windows\DevHome"; + private const string PoliciesPath = @"\SOFTWARE\Policies\DevHome"; // Registry value names private const string PolicyConfigureEnabledDevHome = "ConfigureEnabledDevHome"; From 578be73a7ee4e9deaa3082d5a789cf7e0276e68f Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:28:06 -0400 Subject: [PATCH 3/6] Make all of Dev Home disablable by GPO --- src/Strings/en-us/Resources.resw | 6 ++++++ src/ViewModels/ShellViewModel.cs | 9 +++++++++ src/Views/ShellPage.xaml | 22 +++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Strings/en-us/Resources.resw b/src/Strings/en-us/Resources.resw index f99d469a63..d2d5a36b83 100644 --- a/src/Strings/en-us/Resources.resw +++ b/src/Strings/en-us/Resources.resw @@ -286,4 +286,10 @@ SSH Keychain widget preview image + + Dev Home is blocked + + + Check with your IT or System Administrator for support. + \ No newline at end of file diff --git a/src/ViewModels/ShellViewModel.cs b/src/ViewModels/ShellViewModel.cs index 717ce6022c..8853a6a8fe 100644 --- a/src/ViewModels/ShellViewModel.cs +++ b/src/ViewModels/ShellViewModel.cs @@ -35,6 +35,9 @@ public partial class ShellViewModel : ObservableObject [ObservableProperty] private InfoBarModel _shellInfoBarModel = new(); + [ObservableProperty] + private bool _isDevHomeGPOEnabled; + public ShellViewModel( INavigationService navigationService, INavigationViewService navigationViewService, @@ -59,6 +62,12 @@ public void OnLoaded() Log.Information($"Activated with kind {activationKind}"); TelemetryFactory.Get().Log("DevHome_Shell_Loaded_Event", LogLevel.Critical, new DevHomeShellLoadedEvent(activationKind)); + IsDevHomeGPOEnabled = GPOHelper.GetConfiguredEnabledDevHomeValue(); + if (!IsDevHomeGPOEnabled) + { + return; + } + switch (activationKind) { case ExtendedActivationKind.File: diff --git a/src/Views/ShellPage.xaml b/src/Views/ShellPage.xaml index 1775d90694..d52fe57698 100644 --- a/src/Views/ShellPage.xaml +++ b/src/Views/ShellPage.xaml @@ -6,7 +6,11 @@ xmlns:behaviors="using:DevHome.Common.Behaviors" xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:windows="using:DevHome.Common.Windows" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" Loaded="OnLoaded"> + + + @@ -30,7 +34,8 @@ OpenPaneLength="350" ExpandedModeThresholdWidth="1280" DisplayModeChanged="NavigationViewControl_DisplayModeChanged" - Header="{x:Bind ((ContentControl)ViewModel.Selected).Content, Mode=OneWay}"> + Header="{x:Bind ((ContentControl)ViewModel.Selected).Content, Mode=OneWay}" + Visibility="{x:Bind ViewModel.IsDevHomeGPOEnabled, Mode=OneWay}"> @@ -115,6 +120,21 @@ + + + + + From 7619066148278b3e035c28933e556b6fedadec64 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Fri, 18 Oct 2024 12:44:43 -0400 Subject: [PATCH 4/6] Configuration files --- src/gpo/assets/DevHome.admx | 30 +++++++++++++++++++++++++++++ src/gpo/assets/en-US/DevHome.adml | 32 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/gpo/assets/DevHome.admx create mode 100644 src/gpo/assets/en-US/DevHome.adml diff --git a/src/gpo/assets/DevHome.admx b/src/gpo/assets/DevHome.admx new file mode 100644 index 0000000000..6325fffa8a --- /dev/null +++ b/src/gpo/assets/DevHome.admx @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gpo/assets/en-US/DevHome.adml b/src/gpo/assets/en-US/DevHome.adml new file mode 100644 index 0000000000..92d7e07673 --- /dev/null +++ b/src/gpo/assets/en-US/DevHome.adml @@ -0,0 +1,32 @@ + + + + Dev Home + Dev Home + + + Microsoft Dev Home + + Dev Home version 0.1900.* or later + + This policy configures the enabled state for Dev Home. + +If you enable this setting, Dev Home will be always enabled and the user won't be able to disable it. + +If you disable this setting, Dev Home will be always disabled and the user won't be able to enable it. + +If you don't configure this setting, users are able to enable or disable Dev Home. + +This policy will override any enabled state policies for individual Dev Home features. + + + Configure Dev Home enabled state + + + + + + + + From 8519825588107df581ae8899449812485ab049e9 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:12:55 -0400 Subject: [PATCH 5/6] fix administrative templates --- src/gpo/assets/DevHome.admx | 2 ++ src/gpo/assets/en-US/DevHome.adml | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gpo/assets/DevHome.admx b/src/gpo/assets/DevHome.admx index 6325fffa8a..e540dc25f8 100644 --- a/src/gpo/assets/DevHome.admx +++ b/src/gpo/assets/DevHome.admx @@ -13,6 +13,8 @@ + + diff --git a/src/gpo/assets/en-US/DevHome.adml b/src/gpo/assets/en-US/DevHome.adml index 92d7e07673..e6414b8468 100644 --- a/src/gpo/assets/en-US/DevHome.adml +++ b/src/gpo/assets/en-US/DevHome.adml @@ -24,9 +24,6 @@ This policy will override any enabled state policies for individual Dev Home fea Configure Dev Home enabled state - - - From a583bab148aa6337ec2fa85cf9dd9b881c5b58c0 Mon Sep 17 00:00:00 2001 From: Kristen Schau <47155823+krschau@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:48:45 -0500 Subject: [PATCH 6/6] add logging, handle unspecified case differently --- common/Helpers/GPOHelper.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/common/Helpers/GPOHelper.cs b/common/Helpers/GPOHelper.cs index 2f89e6555d..53aec7ed8c 100644 --- a/common/Helpers/GPOHelper.cs +++ b/common/Helpers/GPOHelper.cs @@ -2,11 +2,14 @@ // Licensed under the MIT License. using Microsoft.Win32; +using Serilog; namespace DevHome.Common.Helpers; public class GPOHelper { + private static readonly ILogger _log = Log.ForContext("SourceContext", nameof(GPOHelper)); + private enum GpoRuleConfigured { WrongValue = -3, // The policy is set to an unrecognized value @@ -32,12 +35,14 @@ private static GpoRuleConfigured GetConfiguredValue(string registryValueName) valueName: registryValueName, defaultValue: GpoRuleConfigured.NotConfigured); + _log.Error($"Registry value {registryValueName} set to {rawValue}"); + // Value will be null if the subkey specified by keyName does not exist. if (rawValue == null) { return GpoRuleConfigured.NotConfigured; } - else if (rawValue is not int) + else if (rawValue is not int && rawValue is not GpoRuleConfigured) { return GpoRuleConfigured.WrongValue; } @@ -68,6 +73,7 @@ private static bool EvaluateConfiguredValue(string registryValueName, GpoRuleCon var configuredValue = GetConfiguredValue(registryValueName); if (configuredValue < 0) { + _log.Error($"Registry value {registryValueName} set to {configuredValue}, using default {defaultValue} instead."); configuredValue = defaultValue; }