Skip to content

Commit

Permalink
fix: Allow system administrators to suppress telemetry by policy (#1204)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaveTryon authored Sep 29, 2021
1 parent 016659d commit 4f3488b
Show file tree
Hide file tree
Showing 14 changed files with 121 additions and 36 deletions.
27 changes: 25 additions & 2 deletions docs/TelemetryOverview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,11 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="320"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource TxtTelemetrySettingInfo}" TextWrapping="Wrap">
<TextBlock Grid.Row="0" Name="telemetryDescription" Style="{StaticResource TxtTelemetrySettingInfo}" TextWrapping="Wrap">
<Run Text="{x:Static Properties:Resources.TextBlockTelemetrySettingInfo}"/>
<LineBreak/>
</TextBlock>
<sharedControls:PrivacyLearnMore Style="{StaticResource TxtTelemetrySettingInfo}" Grid.Row="1"/>
<sharedControls:PrivacyLearnMore x:Name="privacyLearnMore" Style="{StaticResource TxtTelemetrySettingInfo}" Grid.Row="1"/>
</Grid>
<Grid Grid.Row="2" Margin="1,5,0,0">
<Grid.ColumnDefinitions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/AccessibilityInsights.SharedUx/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1548,4 +1548,7 @@ Do you want to change the channel? </value>
<data name="closeDialogText" xml:space="preserve">
<value>Close</value>
</data>
<data name="ApplicationSettingsControl_TelemetryDisabledByAdministrator" xml:space="preserve">
<value>Telemetry has been disabled by an administrator of this computer.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ namespace AccessibilityInsights.SharedUx.Telemetry
{
internal interface ITelemetrySink
{
/// <summary>
/// We allow group policy to disable telemetry. This takes precedence over user opt-in
/// </summary>
bool DoesGroupPolicyAllowTelemetry { get; }

/// <summary>
/// Whether or not telemetry toggle button is enabled in the settings.
/// </summary>
bool IsTelemetryAllowed { get; set; }
bool HasUserOptedIntoTelemetry { get; set; }

/// <summary>
/// Whether or not telemetry is enabled. Exposed to allow callers who do lots of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
27 changes: 22 additions & 5 deletions src/AccessibilityInsights.SharedUx/Telemetry/TelemetrySink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -19,24 +20,40 @@ internal class TelemetrySink : ITelemetrySink
/// <summary>
/// Holds the production TelemetrySink object
/// </summary>
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);
}

/// <summary>
/// Implements <see cref="ITelemetrySink.DoesGroupPolicyAllowTelemetry"/>
/// </summary>
public bool DoesGroupPolicyAllowTelemetry { get; }

/// <summary>
/// Implements <see cref="ITelemetrySink.IsTelemetryAllowed"/>
/// Implements <see cref="ITelemetrySink.HasUserOptedIntoTelemetry"/>
/// </summary>
public bool IsTelemetryAllowed { get; set; }
public bool HasUserOptedIntoTelemetry { get; set; }

/// <summary>
/// Implements <see cref="ITelemetrySink.IsEnabled"/>
/// </summary>
public bool IsEnabled => IsTelemetryAllowed && _telemetry != null;
public bool IsEnabled => DoesGroupPolicyAllowTelemetry && HasUserOptedIntoTelemetry && _telemetry != null;

/// <summary>
/// Implements <see cref="ITelemetrySink.PublishTelemetryEvent(string, string, string)"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,30 @@ public class TelemetrySinkUnitTests
public void BeforeEachTest()
{
_telemetryMock = new Mock<ITelemetry>(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);
}

[TestMethod]
[Timeout(1000)]
public void IsTelemetryAllowed_DefaultToFalse()
{
Assert.IsFalse(_telemetrySink.IsTelemetryAllowed);
Assert.IsFalse(_telemetrySink.HasUserOptedIntoTelemetry);
}

[TestMethod]
Expand All @@ -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);
}

Expand All @@ -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<PropertyBag>()))
.Callback<string, PropertyBag>((_, p) => actualPropertyBag = p);

Expand All @@ -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<PropertyBag>()))
.Throws(expectedExpection);
_telemetryMock.Setup(x => x.ReportException(expectedExpection));
Expand Down Expand Up @@ -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);

Expand All @@ -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));
Expand All @@ -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);
Expand All @@ -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));
Expand All @@ -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);
}

Expand All @@ -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);
Expand All @@ -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);

Expand Down
9 changes: 6 additions & 3 deletions src/AccessibilityInsights/MainWindowHelpers/Initialize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -16,7 +16,8 @@ public partial class MainWindow
/// </summary>
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());
Expand Down

0 comments on commit 4f3488b

Please sign in to comment.