From 4f3488bd5b7cfc284c1094a7c584f236fbe10897 Mon Sep 17 00:00:00 2001
From: Dave Tryon <45672944+DaveTryon@users.noreply.github.com>
Date: Wed, 29 Sep 2021 16:27:24 -0700
Subject: [PATCH] fix: Allow system administrators to suppress telemetry by
policy (#1204)
---
docs/TelemetryOverview.md | 27 +++++++++++++--
.../ApplicationSettingsControl.xaml | 4 +--
.../ApplicationSettingsControl.xaml.cs | 8 +++++
.../TelemetryApproveContainedDialog.xaml.cs | 4 +--
.../Properties/Resources.Designer.cs | 9 +++++
.../Properties/Resources.resx | 3 ++
.../Settings/ConfigurationModel.cs | 2 +-
.../Telemetry/ITelemetrySink.cs | 7 +++-
.../Telemetry/TelemetryController.cs | 16 ++++++---
.../Telemetry/TelemetrySink.cs | 27 ++++++++++++---
.../Settings/ConfigurationModelTests.cs | 2 +-
.../Telemetry/TelemetrySinkUnitTests.cs | 34 ++++++++++++-------
.../MainWindowHelpers/Initialize.cs | 9 +++--
.../MainWindowHelpers/TelemetryStartup.cs | 5 +--
14 files changed, 121 insertions(+), 36 deletions(-)
diff --git a/docs/TelemetryOverview.md b/docs/TelemetryOverview.md
index 44ef369c8..7b95282ca 100644
--- a/docs/TelemetryOverview.md
+++ b/docs/TelemetryOverview.md
@@ -45,8 +45,31 @@ catch (Exception e)
}
```
-### User control
-All telemetry (including reported exceptions) is under user control. When the application starts the first time, the user is allowed to enable or disable telemetry. The user can change this option at any time via the settings page.
+### Control of Telemery
+Telemetry (including reported exceptions) can be disabled either by the computer's administrator or the current user.
+
+#### Administrative override
+An administrator can disable telemetry for all users of a system by adding a DWORD value the registry:
+
+```
+Key: HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Accessibility Insights for Windows
+Name: DisableTelemetry
+Value (DWORD): 1
+```
+Here is the same data expressed as a Windows REG file:
+```
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Accessibility Insights for Windows]
+"DisableTelemetry"=dword:00000001
+```
+
+Setting this override will disable telemetry for _all_ users of the computer. Since telemetry is disabled systemwide, users are never asked to enable or disable telemetry. The overridden status is reflected in the settings page within the application.
+
+This override exists to support organizations who may be sensitive to any data being collected by outside systems. It is the responsibility of the organization to set this flag through external means.
+
+#### User Control
+If no [administrative override](#administrative-override) is set, then telemetry is controlled by each user. When the application starts the first time, the user is asked to enable or disable telemetry. The user can change this setting at any time via the settings page. If the [administrative override](#administrative-override) is later enabled, it will overide the telemetry selection for new application sessions for all users.
### More details
More details are available in the [Telemetry Details Documentation](./TelemetryDetails.md)
\ No newline at end of file
diff --git a/src/AccessibilityInsights.SharedUx/Controls/SettingsTabs/ApplicationSettingsControl.xaml b/src/AccessibilityInsights.SharedUx/Controls/SettingsTabs/ApplicationSettingsControl.xaml
index 4fb7f1b2c..008c14293 100644
--- a/src/AccessibilityInsights.SharedUx/Controls/SettingsTabs/ApplicationSettingsControl.xaml
+++ b/src/AccessibilityInsights.SharedUx/Controls/SettingsTabs/ApplicationSettingsControl.xaml
@@ -263,11 +263,11 @@
-
+
-
+
diff --git a/src/AccessibilityInsights.SharedUx/Controls/SettingsTabs/ApplicationSettingsControl.xaml.cs b/src/AccessibilityInsights.SharedUx/Controls/SettingsTabs/ApplicationSettingsControl.xaml.cs
index bf3436ce4..513883c97 100644
--- a/src/AccessibilityInsights.SharedUx/Controls/SettingsTabs/ApplicationSettingsControl.xaml.cs
+++ b/src/AccessibilityInsights.SharedUx/Controls/SettingsTabs/ApplicationSettingsControl.xaml.cs
@@ -6,6 +6,7 @@
using AccessibilityInsights.SharedUx.Dialogs;
using AccessibilityInsights.SharedUx.Enums;
using AccessibilityInsights.SharedUx.Settings;
+using AccessibilityInsights.SharedUx.Telemetry;
using AccessibilityInsights.SharedUx.ViewModels;
using System;
using System.Collections.Generic;
@@ -71,6 +72,13 @@ public ApplicationSettingsControl()
this.btnHotkeyToggle,
this.btnHotkeyToParent
};
+ if (!TelemetryController.DoesGroupPolicyAllowTelemetry)
+ {
+ this.telemetryDescription.Text = Properties.Resources.ApplicationSettingsControl_TelemetryDisabledByAdministrator;
+ this.privacyLearnMore.Visibility = Visibility.Collapsed;
+ this.lblEnableTelemetryLabel.Visibility = Visibility.Collapsed;
+ this.tgbtnEnableTelemetry.Visibility = Visibility.Collapsed;
+ }
}
///
diff --git a/src/AccessibilityInsights.SharedUx/Dialogs/TelemetryApproveContainedDialog.xaml.cs b/src/AccessibilityInsights.SharedUx/Dialogs/TelemetryApproveContainedDialog.xaml.cs
index 6c5621321..70de84f87 100644
--- a/src/AccessibilityInsights.SharedUx/Dialogs/TelemetryApproveContainedDialog.xaml.cs
+++ b/src/AccessibilityInsights.SharedUx/Dialogs/TelemetryApproveContainedDialog.xaml.cs
@@ -55,9 +55,9 @@ private void btnExit_Click(object sender, RoutedEventArgs e)
DialogResult = ckbxAgreeToHelp.IsChecked ?? false;
if (DialogResult)
- TelemetryController.EnableTelemetry();
+ TelemetryController.OptIntoTelemetry();
else
- TelemetryController.DisableTelemetry();
+ TelemetryController.OptOutOfTelemetry();
WaitHandle.Set();
}
diff --git a/src/AccessibilityInsights.SharedUx/Properties/Resources.Designer.cs b/src/AccessibilityInsights.SharedUx/Properties/Resources.Designer.cs
index d48fd2f44..ae6df5298 100644
--- a/src/AccessibilityInsights.SharedUx/Properties/Resources.Designer.cs
+++ b/src/AccessibilityInsights.SharedUx/Properties/Resources.Designer.cs
@@ -151,6 +151,15 @@ public static string ApplicationSettingsControl_MouseIconAutomationName {
}
}
+ ///
+ /// Looks up a localized string similar to Telemetry has been disabled by an administrator of this computer..
+ ///
+ public static string ApplicationSettingsControl_TelemetryDisabledByAdministrator {
+ get {
+ return ResourceManager.GetString("ApplicationSettingsControl_TelemetryDisabledByAdministrator", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Application settings.
///
diff --git a/src/AccessibilityInsights.SharedUx/Properties/Resources.resx b/src/AccessibilityInsights.SharedUx/Properties/Resources.resx
index aefbb49e0..00b46a531 100644
--- a/src/AccessibilityInsights.SharedUx/Properties/Resources.resx
+++ b/src/AccessibilityInsights.SharedUx/Properties/Resources.resx
@@ -1548,4 +1548,7 @@ Do you want to change the channel?
Close
+
+ Telemetry has been disabled by an administrator of this computer.
+
\ No newline at end of file
diff --git a/src/AccessibilityInsights.SharedUx/Settings/ConfigurationModel.cs b/src/AccessibilityInsights.SharedUx/Settings/ConfigurationModel.cs
index 924d86a65..def99c8e6 100644
--- a/src/AccessibilityInsights.SharedUx/Settings/ConfigurationModel.cs
+++ b/src/AccessibilityInsights.SharedUx/Settings/ConfigurationModel.cs
@@ -631,7 +631,7 @@ public static ConfigurationModel GetDefaultConfigurationModel(FixedConfigSetting
FontSize = FontSize.Standard,
HighlighterMode = HighlighterMode.HighlighterBeakerTooltip,
ShowAncestry = true,
- EnableTelemetry = true,
+ EnableTelemetry = false,
ShowTelemetryDialog = true,
IsUnderElementScope = true,
diff --git a/src/AccessibilityInsights.SharedUx/Telemetry/ITelemetrySink.cs b/src/AccessibilityInsights.SharedUx/Telemetry/ITelemetrySink.cs
index ce2305c71..ea9e83d4c 100644
--- a/src/AccessibilityInsights.SharedUx/Telemetry/ITelemetrySink.cs
+++ b/src/AccessibilityInsights.SharedUx/Telemetry/ITelemetrySink.cs
@@ -7,10 +7,15 @@ namespace AccessibilityInsights.SharedUx.Telemetry
{
internal interface ITelemetrySink
{
+ ///
+ /// We allow group policy to disable telemetry. This takes precedence over user opt-in
+ ///
+ bool DoesGroupPolicyAllowTelemetry { get; }
+
///
/// Whether or not telemetry toggle button is enabled in the settings.
///
- bool IsTelemetryAllowed { get; set; }
+ bool HasUserOptedIntoTelemetry { get; set; }
///
/// Whether or not telemetry is enabled. Exposed to allow callers who do lots of
diff --git a/src/AccessibilityInsights.SharedUx/Telemetry/TelemetryController.cs b/src/AccessibilityInsights.SharedUx/Telemetry/TelemetryController.cs
index 2d0fab9ca..9010f3e08 100644
--- a/src/AccessibilityInsights.SharedUx/Telemetry/TelemetryController.cs
+++ b/src/AccessibilityInsights.SharedUx/Telemetry/TelemetryController.cs
@@ -8,10 +8,13 @@ public static class TelemetryController
{
private static readonly ITelemetrySink Sink = TelemetrySink.DefaultTelemetrySink;
- public static void EnableTelemetry()
+ public static void OptIntoTelemetry()
{
+ if (!DoesGroupPolicyAllowTelemetry)
+ return;
+
// Open the telemetry sink
- Sink.IsTelemetryAllowed = true;
+ Sink.HasUserOptedIntoTelemetry = true;
// Begin listening for telemetry events
// This must be done after the low-level sink is opened above
@@ -21,10 +24,15 @@ public static void EnableTelemetry()
AxeWindowsTelemetrySink.Enable();
}
- public static void DisableTelemetry()
+ public static void OptOutOfTelemetry()
{
+ if (!DoesGroupPolicyAllowTelemetry)
+ return;
+
// Close the telemetry sink
- Sink.IsTelemetryAllowed = false;
+ Sink.HasUserOptedIntoTelemetry = false;
}
+
+ public static bool DoesGroupPolicyAllowTelemetry => Sink.DoesGroupPolicyAllowTelemetry;
} // class
} // namespace
diff --git a/src/AccessibilityInsights.SharedUx/Telemetry/TelemetrySink.cs b/src/AccessibilityInsights.SharedUx/Telemetry/TelemetrySink.cs
index 9e9f0daeb..b7300dfe3 100644
--- a/src/AccessibilityInsights.SharedUx/Telemetry/TelemetrySink.cs
+++ b/src/AccessibilityInsights.SharedUx/Telemetry/TelemetrySink.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using AccessibilityInsights.Extensions;
using AccessibilityInsights.Extensions.Interfaces.Telemetry;
+using Microsoft.Win32;
using System;
using System.Collections.Generic;
@@ -19,24 +20,40 @@ internal class TelemetrySink : ITelemetrySink
///
/// Holds the production TelemetrySink object
///
- internal static ITelemetrySink DefaultTelemetrySink { get; } = new TelemetrySink(Container.GetDefaultInstance()?.Telemetry);
+ internal static ITelemetrySink DefaultTelemetrySink { get; } = new TelemetrySink(Container.GetDefaultInstance()?.Telemetry, DoesRegistryGroupPolicyAllowTelemetry());
private readonly ITelemetry _telemetry;
- internal TelemetrySink(ITelemetry telemetry)
+ internal TelemetrySink(ITelemetry telemetry, bool doesGroupPolicyAllowTelemetry)
{
_telemetry = telemetry;
+ DoesGroupPolicyAllowTelemetry = doesGroupPolicyAllowTelemetry;
}
+ private static bool DoesRegistryGroupPolicyAllowTelemetry()
+ {
+ // Return true unless the policy exists to disable the telemetry
+ int? policyValue = (int?)Registry.GetValue(
+ @"HKEY_LOCAL_MACHINE\Software\Policies\Accessibility Insights for Windows",
+ "DisableTelemetry", 0);
+
+ return !(policyValue.HasValue && policyValue.Value == 1);
+ }
+
+ ///
+ /// Implements
+ ///
+ public bool DoesGroupPolicyAllowTelemetry { get; }
+
///
- /// Implements
+ /// Implements
///
- public bool IsTelemetryAllowed { get; set; }
+ public bool HasUserOptedIntoTelemetry { get; set; }
///
/// Implements
///
- public bool IsEnabled => IsTelemetryAllowed && _telemetry != null;
+ public bool IsEnabled => DoesGroupPolicyAllowTelemetry && HasUserOptedIntoTelemetry && _telemetry != null;
///
/// Implements
diff --git a/src/AccessibilityInsights.SharedUxTests/Settings/ConfigurationModelTests.cs b/src/AccessibilityInsights.SharedUxTests/Settings/ConfigurationModelTests.cs
index 49e085d18..652b17d2c 100644
--- a/src/AccessibilityInsights.SharedUxTests/Settings/ConfigurationModelTests.cs
+++ b/src/AccessibilityInsights.SharedUxTests/Settings/ConfigurationModelTests.cs
@@ -92,7 +92,7 @@ public void LoadFromJSON_FileDoesNotExist_DataIsCorrect()
ConfirmEnumerablesMatchExpectations(new int[] { }, config.CoreTPAttributes.ToArray());
Assert.IsFalse(config.DisableTestsInSnapMode);
Assert.IsFalse(config.DisableDarkMode);
- Assert.IsTrue(config.EnableTelemetry);
+ Assert.IsFalse(config.EnableTelemetry);
Assert.IsTrue(config.EventRecordPath.Equals(testProvider.UserDataFolderPath));
Assert.AreEqual(FontSize.Standard, config.FontSize);
Assert.AreEqual(HighlighterMode.HighlighterBeakerTooltip, config.HighlighterMode);
diff --git a/src/AccessibilityInsights.SharedUxTests/Telemetry/TelemetrySinkUnitTests.cs b/src/AccessibilityInsights.SharedUxTests/Telemetry/TelemetrySinkUnitTests.cs
index 0080127f3..4494d88e1 100644
--- a/src/AccessibilityInsights.SharedUxTests/Telemetry/TelemetrySinkUnitTests.cs
+++ b/src/AccessibilityInsights.SharedUxTests/Telemetry/TelemetrySinkUnitTests.cs
@@ -28,14 +28,22 @@ public class TelemetrySinkUnitTests
public void BeforeEachTest()
{
_telemetryMock = new Mock(MockBehavior.Strict);
- _telemetrySink = new TelemetrySink(_telemetryMock.Object);
+ _telemetrySink = new TelemetrySink(_telemetryMock.Object, true);
}
[TestMethod]
[Timeout(1000)]
public void IsEnabled_TelemetryIsNull_ReturnsFalse()
{
- TelemetrySink sink = new TelemetrySink(null);
+ TelemetrySink sink = new TelemetrySink(null, true);
+ Assert.IsFalse(sink.IsEnabled);
+ }
+
+ [TestMethod]
+ [Timeout(1000)]
+ public void IsEnabled_TelemetryIsDisabledByGroupPolicy_ReturnsFalse()
+ {
+ TelemetrySink sink = new TelemetrySink(_telemetryMock.Object, false);
Assert.IsFalse(sink.IsEnabled);
}
@@ -43,7 +51,7 @@ public void IsEnabled_TelemetryIsNull_ReturnsFalse()
[Timeout(1000)]
public void IsTelemetryAllowed_DefaultToFalse()
{
- Assert.IsFalse(_telemetrySink.IsTelemetryAllowed);
+ Assert.IsFalse(_telemetrySink.HasUserOptedIntoTelemetry);
}
[TestMethod]
@@ -57,7 +65,7 @@ public void IsEnabled_IsTelemetryAllowedIsFalse_ReturnsFalse()
[Timeout(1000)]
public void IsEnabled_IsTelemetryAllowedIsTrue_ReturnsTrue()
{
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
Assert.IsTrue(_telemetrySink.IsEnabled);
}
@@ -74,7 +82,7 @@ public void PublishTelemetryEvent_SingleProperty_TelemetryAllowed_PublishesCorre
{
PropertyBag actualPropertyBag = null;
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetryMock.Setup(x => x.PublishEvent(EventName1, It.IsAny()))
.Callback((_, p) => actualPropertyBag = p);
@@ -90,7 +98,7 @@ public void PublishTelemetryEvent_SingleProperty_TelemetryAllowed_PublishesCorre
public void PublishTelemetryEvent_SingleProperty_TelemetryAllowed_ThrowsOnPublish_ReportsException()
{
Exception expectedExpection = new OutOfMemoryException();
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetryMock.Setup(x => x.PublishEvent(EventName1, It.IsAny()))
.Throws(expectedExpection);
_telemetryMock.Setup(x => x.ReportException(expectedExpection));
@@ -123,7 +131,7 @@ public void PublishTelemetryEvent_PropertyBag_TelemetryAllowed_ChainsSameData()
{ PropertyName2, Value2 },
};
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetryMock.Setup(x => x.PublishEvent(EventName2, expectedPropertyBag));
_telemetrySink.PublishTelemetryEvent(EventName2, expectedPropertyBag);
@@ -141,7 +149,7 @@ public void PublishTelemetryEvent_PropertyBag_TelemetryAllowed_ThrowsOnPublish_R
{ PropertyName2, Value1 },
};
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetryMock.Setup(x => x.PublishEvent(EventName2, expectedPropertyBag))
.Throws(expectedExpection);
_telemetryMock.Setup(x => x.ReportException(expectedExpection));
@@ -162,7 +170,7 @@ public void AddOrUpdateContextProperty_TelemetryNotAllowed_DoesNotChain()
[Timeout(1000)]
public void AddOrUpdateContextProperty_TelemetryIsAllowed_ChainsSameData()
{
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetryMock.Setup(x => x.AddOrUpdateContextProperty(PropertyName2, Value2));
_telemetrySink.AddOrUpdateContextProperty(PropertyName2, Value2);
@@ -175,7 +183,7 @@ public void AddOrUpdateContextProperty_TelemetryIsAllowed_ChainsSameData()
public void AddOrUpdateContextProperty_TelemetryIsAllowed_TelemetryThrowsException_ReportsException()
{
Exception expectedExpection = new OutOfMemoryException();
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetryMock.Setup(x => x.AddOrUpdateContextProperty(PropertyName2, Value2))
.Throws(expectedExpection);
_telemetryMock.Setup(x => x.ReportException(expectedExpection));
@@ -196,7 +204,7 @@ public void ReportException_TelemetryNotAllowed_DoesNotChain()
[Timeout(1000)]
public void ReportException_TelemetryIsAllowed_ExceptionIsNull_DoesNotChain()
{
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetrySink.ReportException(null);
}
@@ -206,7 +214,7 @@ public void ReportException_TelemetryIsAllowed_ChainsSameData()
{
Exception expectedException = new OutOfMemoryException();
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetryMock.Setup(x => x.ReportException(expectedException));
_telemetrySink.ReportException(expectedException);
@@ -221,7 +229,7 @@ public void ReportException_TelemetryIsAllowed_TelemetryThrowsException_DoesNotR
Exception expectedException = new OutOfMemoryException();
Exception unexpectedException = new InvalidOperationException();
- _telemetrySink.IsTelemetryAllowed = true;
+ _telemetrySink.HasUserOptedIntoTelemetry = true;
_telemetryMock.Setup(x => x.ReportException(expectedException))
.Throws(unexpectedException);
diff --git a/src/AccessibilityInsights/MainWindowHelpers/Initialize.cs b/src/AccessibilityInsights/MainWindowHelpers/Initialize.cs
index 2ce55f4b7..78a5bb14b 100644
--- a/src/AccessibilityInsights/MainWindowHelpers/Initialize.cs
+++ b/src/AccessibilityInsights/MainWindowHelpers/Initialize.cs
@@ -141,9 +141,12 @@ private static void PopulateConfigurations(TelemetryBuffer telemetryBuffer)
// Configure the correct ReleaseChannel for autoupdate
Container.SetAutoUpdateReleaseChannel(ConfigurationManager.GetDefaultInstance().AppConfig.ReleaseChannel.ToString());
- // enable/disable telemetry
- if (ConfigurationManager.GetDefaultInstance().AppConfig.EnableTelemetry)
- TelemetryController.EnableTelemetry();
+ // Opt into telemetry if allowed and user has chosen to do so
+ if (TelemetryController.DoesGroupPolicyAllowTelemetry &&
+ ConfigurationManager.GetDefaultInstance().AppConfig.EnableTelemetry)
+ {
+ TelemetryController.OptIntoTelemetry();
+ }
// Update theming since it depends on config options
SetColorTheme();
diff --git a/src/AccessibilityInsights/MainWindowHelpers/TelemetryStartup.cs b/src/AccessibilityInsights/MainWindowHelpers/TelemetryStartup.cs
index 2700e2164..52e1e3cfe 100644
--- a/src/AccessibilityInsights/MainWindowHelpers/TelemetryStartup.cs
+++ b/src/AccessibilityInsights/MainWindowHelpers/TelemetryStartup.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using AccessibilityInsights.SharedUx.Dialogs;
using AccessibilityInsights.SharedUx.Settings;
-using System.Windows;
+using AccessibilityInsights.SharedUx.Telemetry;
namespace AccessibilityInsights
{
@@ -16,7 +16,8 @@ public partial class MainWindow
///
private void ShowTelemetryDialog()
{
- if (ConfigurationManager.GetDefaultInstance().AppConfig.ShowTelemetryDialog)
+ if (TelemetryController.DoesGroupPolicyAllowTelemetry &&
+ ConfigurationManager.GetDefaultInstance().AppConfig.ShowTelemetryDialog)
{
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
ctrlDialogContainer.ShowDialog(new TelemetryApproveContainedDialog());