From beaf4735ad2b38aa64a7fa4021ea1768cb6cac72 Mon Sep 17 00:00:00 2001
From: Kristen Schau <47155823+krschau@users.noreply.github.com>
Date: Fri, 8 Nov 2024 15:18:02 -0500
Subject: [PATCH] Implement GPO and add administrative templates (#3965)
---
common/Helpers/GPOHelper.cs | 88 +++++++++++++++++++++++++++++++
src/Strings/en-us/Resources.resw | 6 +++
src/ViewModels/ShellViewModel.cs | 9 ++++
src/Views/ShellPage.xaml | 22 +++++++-
src/gpo/assets/DevHome.admx | 32 +++++++++++
src/gpo/assets/en-US/DevHome.adml | 29 ++++++++++
6 files changed, 185 insertions(+), 1 deletion(-)
create mode 100644 common/Helpers/GPOHelper.cs
create mode 100644 src/gpo/assets/DevHome.admx
create mode 100644 src/gpo/assets/en-US/DevHome.adml
diff --git a/common/Helpers/GPOHelper.cs b/common/Helpers/GPOHelper.cs
new file mode 100644
index 0000000000..53aec7ed8c
--- /dev/null
+++ b/common/Helpers/GPOHelper.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Microsoft Corporation.
+// 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
+ 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\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);
+
+ _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 && rawValue is not GpoRuleConfigured)
+ {
+ 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)
+ {
+ _log.Error($"Registry value {registryValueName} set to {configuredValue}, using default {defaultValue} instead.");
+ configuredValue = defaultValue;
+ }
+
+ return configuredValue == GpoRuleConfigured.Enabled;
+ }
+
+ public static bool GetConfiguredEnabledDevHomeValue()
+ {
+ var defaultValue = GpoRuleConfigured.Enabled;
+ return EvaluateConfiguredValue(PolicyConfigureEnabledDevHome, defaultValue);
+ }
+}
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 @@
+
+
+
+
+
diff --git a/src/gpo/assets/DevHome.admx b/src/gpo/assets/DevHome.admx
new file mode 100644
index 0000000000..e540dc25f8
--- /dev/null
+++ b/src/gpo/assets/DevHome.admx
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/gpo/assets/en-US/DevHome.adml b/src/gpo/assets/en-US/DevHome.adml
new file mode 100644
index 0000000000..e6414b8468
--- /dev/null
+++ b/src/gpo/assets/en-US/DevHome.adml
@@ -0,0 +1,29 @@
+
+
+
+ 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
+
+
+
+
+