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 + + + + +