From e69aa95beb92161b92f1ec4675e2bf6ef18b9655 Mon Sep 17 00:00:00 2001 From: Hirsch Singhal Date: Fri, 5 May 2017 10:42:35 -0700 Subject: [PATCH] v0.9.4.1 Beta Release - re-add CSRF handling, formatting fixes (#241) * Update content for WER * Update docs for XML. * Update Copyright to 2017 :) --- README.md | 2 +- Samples/XboxWdpDriver/Program.cs | 7 +- .../UnitTestProject/Core/EtwTests.cs | 5 +- .../IoT/IoT_rs1_release.cs | 44 ++--- .../WDPMockImplementations/WebSocket.cs | 16 +- .../Core/AppDeployment.cs | 2 +- .../Core/AppFileExplorer.cs | 10 +- .../Core/DumpCollection.cs | 187 +++++++++--------- .../Core/Etw.cs | 20 +- .../Core/Power.cs | 5 +- .../Core/WindowsErrorReporting.cs | 7 +- .../DevicePortal.cs | 1 + .../Exceptions/DevicePortalException.cs | 93 ++++----- .../HoloLens/HolographicPerception.cs | 8 +- .../HttpRest/HttpHeadersHelper.cs | 72 +++++++ .../HttpRest/WebSocket.cs | 1 - .../IoT/IoTOnboarding.cs | 4 +- .../IoT/Limpet.cs | 3 + .../IoT/ProcessManagement.cs | 11 +- .../IoT/WindowsUpdate.cs | 4 +- .../Core/AppDeployment.cs | 3 +- .../HttpRest/RequestHelpers.cs | 1 + .../HttpRest/RestDelete.cs | 2 + .../HttpRest/RestGet.cs | 2 + .../HttpRest/RestPost.cs | 2 + .../HttpRest/RestPut.cs | 2 + .../HttpRest/WebSocket.cs | 10 +- .../DefaultDevicePortalConnection.cs | 7 +- .../HttpRest/RestDelete.cs | 2 + .../HttpRest/RestGet.cs | 2 + .../HttpRest/RestPost.cs | 2 + .../HttpRest/RestPut.cs | 2 + 32 files changed, 324 insertions(+), 215 deletions(-) diff --git a/README.md b/README.md index d227ddc6..7ca9b608 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ For documentation on the WDP REST endpoints which the project wraps, see the fol ### HoloLens specific methods -[HoloLens Device Portal API reference](https://developer.microsoft.com/en-us/windows/holographic/device_portal_api_reference) +[HoloLens Device Portal API reference](https://developer.microsoft.com/en-us/windows/mixed-reality/device_portal_api_reference) --- diff --git a/Samples/XboxWdpDriver/Program.cs b/Samples/XboxWdpDriver/Program.cs index d438187d..339b01bc 100644 --- a/Samples/XboxWdpDriver/Program.cs +++ b/Samples/XboxWdpDriver/Program.cs @@ -338,8 +338,11 @@ public static void Main(string[] args) break; case OperationType.InstallOperation: - // Ensure we have an IP since SMB might need it for path generation. - parameters.AddParameter(ParameterHelper.IpOrHostname, targetConsole); + if (!parameters.HasParameter(ParameterHelper.IpOrHostname)) + { + // Ensure we have an IP since SMB might need it for path generation. + parameters.AddParameter(ParameterHelper.IpOrHostname, targetConsole); + } InstallOperation.HandleOperation(portal, parameters); break; diff --git a/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs b/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs index 6774e3da..eb5c3c0f 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs @@ -52,6 +52,9 @@ public void GetEtwProvidersTest() ValidateEtwProviders(getEtwProvidersTask.Result); } + /// + /// Basic test of Get methof for getting ETW events. + /// [TestMethod] public void GetEtwEventsTest() { @@ -61,7 +64,7 @@ public void GetEtwEventsTest() EtwEvents etwEvents = null; WindowsDevicePortal.WebSocketMessageReceivedEventHandler etwEventsReceivedHandler = - delegate (DevicePortal sender, WebSocketMessageReceivedEventArgs args) + delegate(DevicePortal sender, WebSocketMessageReceivedEventArgs args) { if (args.Message != null) { diff --git a/WindowsDevicePortalWrapper/UnitTestProject/Device-VersionTests/IoT/IoT_rs1_release.cs b/WindowsDevicePortalWrapper/UnitTestProject/Device-VersionTests/IoT/IoT_rs1_release.cs index dd12df49..34029f4e 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/Device-VersionTests/IoT/IoT_rs1_release.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/Device-VersionTests/IoT/IoT_rs1_release.cs @@ -4,11 +4,11 @@ // //---------------------------------------------------------------------------------------------- -using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; using static Microsoft.Tools.WindowsDevicePortal.DevicePortal; namespace Microsoft.Tools.WindowsDevicePortal.Tests @@ -307,7 +307,6 @@ public void GetUpdateInstallTime_IoT() // Check some known things about this response. Assert.AreEqual(0, installTime.RebootScheduled); - } /// @@ -396,21 +395,20 @@ public void SetDeviceNameTest_IoT() Assert.AreEqual(TaskStatus.RanToCompletion, setIoTDeviceName.Status); } - /// /// Simple test to set SoftAp Settings /// [TestMethod] public void SetSoftApSettingsTest_IoT() { - string SoftApEnabled = "true"; - string SoftApSsid = "SoftAPSsid"; - string SoftApPassword = "p@ssw0rd"; + string softApEnabled = "true"; + string softApSsid = "SoftAPSsid"; + string softApPassword = "p@ssw0rd"; HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NoContent); TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.SoftAPSettingsApi, response, HttpMethods.Post); - Task setSoftApSettings = TestHelpers.Portal.SetSoftApSettingsAsync(SoftApEnabled, SoftApSsid, SoftApPassword); + Task setSoftApSettings = TestHelpers.Portal.SetSoftApSettingsAsync(softApEnabled, softApSsid, softApPassword); setSoftApSettings.Wait(); Assert.AreEqual(TaskStatus.RanToCompletion, setSoftApSettings.Status); @@ -495,7 +493,7 @@ public void SetControllersDriversTest_IoT() setIoTControllersDrivers.Wait(); Assert.AreEqual(TaskStatus.RanToCompletion, setIoTControllersDrivers.Status); - Assert.AreEqual(requestReboot, setIoTControllersDrivers.Result.RequestReboot ); + Assert.AreEqual(requestReboot, setIoTControllersDrivers.Result.RequestReboot); } /// @@ -694,10 +692,10 @@ public void SetRenderVolumeTest_IoT() HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NoContent); TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.SetRenderVolumeApi, response, HttpMethods.Post); - Task RenderVolume = TestHelpers.Portal.SetRenderVolumeAsync(renderVolume); - RenderVolume.Wait(); + Task renderVolumeTask = TestHelpers.Portal.SetRenderVolumeAsync(renderVolume); + renderVolumeTask.Wait(); - Assert.AreEqual(TaskStatus.RanToCompletion, RenderVolume.Status); + Assert.AreEqual(TaskStatus.RanToCompletion, renderVolumeTask.Status); } /// @@ -711,10 +709,10 @@ public void SetCaptureVolumeTest_IoT() HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NoContent); TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.SetCaptureVolumeApi, response, HttpMethods.Post); - Task CaptureVolume = TestHelpers.Portal.SetCaptureVolumeAsync(captureVolume); - CaptureVolume.Wait(); + Task captureVolumeTask = TestHelpers.Portal.SetCaptureVolumeAsync(captureVolume); + captureVolumeTask.Wait(); - Assert.AreEqual(TaskStatus.RanToCompletion, CaptureVolume.Status); + Assert.AreEqual(TaskStatus.RanToCompletion, captureVolumeTask.Status); } /// @@ -729,10 +727,10 @@ public void IcSharingStartTest_IoT() HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NoContent); TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.IcSharingApi, response, HttpMethods.Post); - Task IcsStart = TestHelpers.Portal.IcSharingStartAsync(privateInterfaceIndex, publicInterfaceIndex); - IcsStart.Wait(); + Task icsStart = TestHelpers.Portal.IcSharingStartAsync(privateInterfaceIndex, publicInterfaceIndex); + icsStart.Wait(); - Assert.AreEqual(TaskStatus.RanToCompletion, IcsStart.Status); + Assert.AreEqual(TaskStatus.RanToCompletion, icsStart.Status); } /// @@ -747,10 +745,10 @@ public void IcSharingStopTest_IoT() HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NoContent); TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.IcSharingApi, response, HttpMethods.Delete); - Task IcsStop = TestHelpers.Portal.IcSharingStopAsync(privateInterfaceIndex, publicInterfaceIndex); - IcsStop.Wait(); + Task icsStop = TestHelpers.Portal.IcSharingStopAsync(privateInterfaceIndex, publicInterfaceIndex); + icsStop.Wait(); - Assert.AreEqual(TaskStatus.RanToCompletion, IcsStop.Status); + Assert.AreEqual(TaskStatus.RanToCompletion, icsStop.Status); } /// @@ -966,8 +964,8 @@ public void SetTpmAcpiTablesTest_IoT() public void SetTpmLogicalDeviceSettingsTest_IoT() { int logicalDeviceId = 1; - string azureUri = ""; - string azureKey = ""; + string azureUri = string.Empty; + string azureKey = string.Empty; HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NoContent); TestHelpers.MockHttpResponder.AddMockResponse(string.Format("{0}/{1}", DevicePortal.TpmSettingsApi, logicalDeviceId), response, HttpMethods.Post); @@ -1005,7 +1003,7 @@ public void GetTpmAzureTokenInfo_IoT() string validity = "18000"; TestHelpers.MockHttpResponder.AddMockResponse( - string.Format("{0}/{1}", TpmAzureTokenApi, logicalDeviceId), + string.Format("{0}/{1}", DevicePortal.TpmAzureTokenApi, logicalDeviceId), this.PlatformType, this.FriendlyOperatingSystemVersion, HttpMethods.Get); diff --git a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs index 5439b300..2b433381 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs @@ -64,7 +64,7 @@ private async Task ConnectInternalAsync(Uri endpoint) { await Task.Run(() => { - webSocketTask = TestHelpers.MockHttpResponder.WebSocketAsync(endpoint); + this.webSocketTask = TestHelpers.MockHttpResponder.WebSocketAsync(endpoint); this.IsConnected = true; }); } @@ -77,8 +77,8 @@ private async Task CloseInternalAsync() { await Task.Run(() => { - webSocketTask.Dispose(); - webSocketTask = null; + this.webSocketTask.Dispose(); + this.webSocketTask = null; this.IsConnected = false; }); } @@ -115,9 +115,9 @@ private async Task StartListeningForMessagesInternalAsync() { while (this.keepListeningForMessages) { - await webSocketTask.ConfigureAwait(false); + await this.webSocketTask.ConfigureAwait(false); - using (HttpResponseMessage response = webSocketTask.Result) + using (HttpResponseMessage response = this.webSocketTask.Result) { if (!response.IsSuccessStatusCode) { @@ -155,10 +155,10 @@ private async Task StartListeningForMessagesInternalAsync() /// The task of sending the message to the websocket private async Task SendMessageInternalAsync(string message) { - await webSocketTask.ConfigureAwait(false); - webSocketTask.Wait(); + await this.webSocketTask.ConfigureAwait(false); + this.webSocketTask.Wait(); - using (HttpResponseMessage response = webSocketTask.Result) + using (HttpResponseMessage response = this.webSocketTask.Result) { if (!response.IsSuccessStatusCode) { diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppDeployment.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppDeployment.cs index 99b34fb5..00dbba77 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppDeployment.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppDeployment.cs @@ -385,7 +385,7 @@ public class PackageInfo /// True if the package is sideloaded. public bool IsSideloaded() { - return (this.PackageOrigin == 4 || this.PackageOrigin == 5); + return this.PackageOrigin == 4 || this.PackageOrigin == 5; } /// diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppFileExplorer.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppFileExplorer.cs index 5829f175..ea27c953 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppFileExplorer.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppFileExplorer.cs @@ -311,10 +311,16 @@ public class FileOrFolderInformation public long SizeInBytes { get; private set; } /// - /// Gets whether the current item is a folder by checking for FILE_ATTRIBUTE_DIRECTORY + /// Gets a value indicating whether the current item is a folder by checking for FILE_ATTRIBUTE_DIRECTORY /// See https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx /// - public bool IsFolder { get { return (this.Type & 0x10) == 0x10; } } + public bool IsFolder + { + get + { + return (this.Type & 0x10) == 0x10; + } + } /// /// Overridden ToString method providing a user readable diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/DumpCollection.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/DumpCollection.cs index 94b32808..641341ef 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/DumpCollection.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/DumpCollection.cs @@ -111,129 +111,132 @@ public async Task GetDumpFileSettingsAsync() /// Task tracking completion of the request public async Task SetDumpFileSettingsAsync(DumpFileSettings dfs) { - await this.PostAsync( - BugcheckSettingsApi, - string.Format( + string queryParams = string.Format( "autoreboot={0}&overwrite={1}&dumptype={2}&maxdumpcount={3}", - dfs.AutoReboot ? "1" : "0", dfs.Overwrite ? "1" : "0", (int)dfs.DumpType, dfs.MaxDumpCount)); - } - } - - #region Data Contract - - /// - /// DumpFileSettings object. Used to get and set how and when a dump is saved on the device. - /// - [DataContract] - public class DumpFileSettings - { - /// - /// Gets or sets whether the device should restart after a crash dump is taken. - /// - [DataMember(Name = "autoreboot")] - public bool AutoReboot { get; set; } + dfs.AutoReboot ? "1" : "0", + dfs.Overwrite ? "1" : "0", + (int)dfs.DumpType, + dfs.MaxDumpCount); - /// - /// Gets or sets the type of dump to be saved when a bugcheck occurs. - /// - [DataMember(Name = "dumptype")] - public DumpTypes DumpType { get; set; } + await this.PostAsync(BugcheckSettingsApi, queryParams); + } - /// - /// Gets or sets the max number of dumps to be saved on the device. - /// - [DataMember(Name = "maxdumpcount")] - public int MaxDumpCount { get; set; } + #region Data Contract /// - /// Gets or sets whether new dumps should overwrite older dumps. + /// DumpFileSettings object. Used to get and set how and when a dump is saved on the device. /// - [DataMember(Name = "overwrite")] - public bool Overwrite { get; set; } - - /// - /// The 3 types of dumps that can be saved on the device (or not saved at all). - /// - public enum DumpTypes + [DataContract] + public class DumpFileSettings { /// - /// Don't collect device crash dumps + /// The 3 types of dumps that can be saved on the device (or not saved at all). /// - Disabled = 0, - + public enum DumpTypes + { + /// + /// Don't collect device crash dumps + /// + Disabled = 0, + + /// + /// Collect all in use memory + /// + CompleteMemoryDump = 1, + + /// + /// Don't include usermode memory in the dump + /// + KernelDump = 2, + + /// + /// Limited kernel dump + /// + Minidump = 3 + } + /// - /// Collect all in use memory + /// Gets or sets a value indicating whether the device should restart after a crash dump is taken. /// - CompleteMemoryDump = 1, + [DataMember(Name = "autoreboot")] + public bool AutoReboot { get; set; } /// - /// Don't include usermode memory in the dump + /// Gets or sets the type of dump to be saved when a bugcheck occurs. /// - KernelDump = 2, - + [DataMember(Name = "dumptype")] + public DumpTypes DumpType { get; set; } + /// - /// Limited kernel dump + /// Gets or sets the max number of dumps to be saved on the device. /// - Minidump = 3 + [DataMember(Name = "maxdumpcount")] + public int MaxDumpCount { get; set; } + + /// + /// Gets or sets a value indicating whether new dumps should overwrite older dumps. + /// + [DataMember(Name = "overwrite")] + public bool Overwrite { get; set; } } - } - /// - /// Gets a list of kernel dumps on the device. - /// - [DataContract] - public class DumpFileList - { /// /// Gets a list of kernel dumps on the device. /// - [DataMember(Name = "DumpFiles")] - public List DumpFiles { get; private set; } - } - - /// - /// Represents a dumpfile stored on the device. - /// - [DataContract] - public class Dumpfile - { - /// - /// Gets the timestamp of the crash as a string. - /// - [DataMember(Name = "FileDate")] - public string FileDateAsString + [DataContract] + public class DumpFileList { - get; private set; + /// + /// Gets a list of kernel dumps on the device. + /// + [DataMember(Name = "DumpFiles")] + public List DumpFiles { get; private set; } } /// - /// Gets the timestamp of the crash. + /// Represents a dumpfile stored on the device. /// - public DateTime FileDate + [DataContract] + public class Dumpfile { - get + /// + /// Gets the timestamp of the crash as a string. + /// + [DataMember(Name = "FileDate")] + public string FileDateAsString { - return DateTime.Parse(this.FileDateAsString); + get; private set; } - } - /// - /// Gets the filename of the crash file. - /// - [DataMember(Name = "FileName")] - public string Filename - { - get; private set; - } + /// + /// Gets the timestamp of the crash. + /// + public DateTime FileDate + { + get + { + return DateTime.Parse(this.FileDateAsString); + } + } - /// - /// Gets the size of the crash dump, in bytes - /// - [DataMember(Name = "FileSize")] - public uint FileSizeInBytes - { - get; private set; + /// + /// Gets the filename of the crash file. + /// + [DataMember(Name = "FileName")] + public string Filename + { + get; private set; + } + + /// + /// Gets the size of the crash dump, in bytes + /// + [DataMember(Name = "FileSize")] + public uint FileSizeInBytes + { + get; private set; + } } + #endregion Data Contract } - #endregion Data Contract } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs index 8b131015..eef4e617 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs @@ -167,19 +167,13 @@ private void EtwEventsReceivedHandler( [DataContract] public class EtwEvents { - /// - /// Gets or sets the raw list of events. Not for straight usage, as it's entirely unformatted. - /// - [DataMember(Name = "Events")] - private List> RawEvents { get; set; } - /// /// Saves the downconverted list of events /// - private List stashedList; + private List stashedList; /// - /// Get the list of ETW Events that occured in the last second. + /// Gets the list of ETW Events that occured in the last second. /// public List Events { @@ -191,7 +185,7 @@ public List Events } List events = new List(); - foreach (Dictionary dic in RawEvents ) + foreach (Dictionary dic in this.RawEvents) { events.Add(new EtwEventInfo(dic)); } @@ -201,13 +195,18 @@ public List Events } } - /// /// Gets the event frequency. /// This is always 10 million (10000000) in RS2 devices. /// [DataMember(Name = "Frequency")] public long Frequency { get; private set; } + + /// + /// Gets or sets the raw list of events. Not for straight usage, as it's entirely unformatted. + /// + [DataMember(Name = "Events")] + private List> RawEvents { get; set; } } /// @@ -217,7 +216,6 @@ public List Events /// public class EtwEventInfo : Dictionary { - /// /// Initializes a new instance of the class. Used by the DataContract at access time. /// diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Power.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Power.cs index e265fbf9..da87fa63 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Power.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Power.cs @@ -155,7 +155,10 @@ public float Level get { // Desktop PCs typically do not have a battery, return 100% - if (this.MaximumCapacity == 0) { return 100f; } + if (this.MaximumCapacity == 0) + { + return 100f; + } return 100.0f * ((float)this.RemainingCapacity / this.MaximumCapacity); } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/WindowsErrorReporting.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/WindowsErrorReporting.cs index cf53ab2e..9124a528 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/WindowsErrorReporting.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/WindowsErrorReporting.cs @@ -156,13 +156,14 @@ public class WerDeviceReports public List UserReports { get; private set; } /// - /// Convenience accessor for the System error reports - this is + /// Gets system error reports - Convenience accessor for the System error reports - this is /// where most error reports end up. /// - public WerUserReports SystemErrorReports { + public WerUserReports SystemErrorReports + { get { - return UserReports.First(x => x.UserName == "SYSTEM"); + return this.UserReports.First(x => x.UserName == "SYSTEM"); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs index ed6278c7..41403c5e 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs @@ -258,6 +258,7 @@ public async Task ConnectAsync( connectionPhaseDescription); bool preservePort = true; + // HoloLens and Mobile are the only devices that support USB. // They require the port to be changed when the connection is updated // to WiFi. diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Exceptions/DevicePortalException.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Exceptions/DevicePortalException.cs index 6fb6f5c7..11eea2a5 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Exceptions/DevicePortalException.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Exceptions/DevicePortalException.cs @@ -66,18 +66,57 @@ public DevicePortalException( } } + /// + /// Initializes a new instance of the class. + /// + /// Http status code. + /// Reason for exception. + /// Request URI which threw the exception. + /// Optional message. + /// Optional inner exception. + public DevicePortalException( + HttpStatusCode statusCode, + string reason, + Uri requestUri = null, + string message = "", + Exception innerException = null) : base( + message, + innerException) + { + this.StatusCode = statusCode; + this.Reason = reason; + this.RequestUri = requestUri; + } + + /// + /// Gets the HTTP Status code. + /// + public HttpStatusCode StatusCode { get; private set; } + + /// + /// Gets a reason for the exception. + /// + public string Reason { get; private set; } + + /// + /// Gets the request URI that threw the exception. + /// + public Uri RequestUri { get; private set; } + /// /// Initializes a new instance of the class. /// /// Http response message. /// Optional exception message. /// Optional inner exception. + /// async task public static async Task CreateAsync( HttpResponseMessage responseMessage, string message = "", Exception innerException = null) { - DevicePortalException error = new DevicePortalException(responseMessage.StatusCode, + DevicePortalException error = new DevicePortalException( + responseMessage.StatusCode, responseMessage.ReasonPhrase, responseMessage.RequestMessage != null ? responseMessage.RequestMessage.RequestUri : null, message, @@ -88,15 +127,15 @@ public static async Task CreateAsync( { Stream dataStream = null; #if !WINDOWS_UWP - using (HttpContent content = responseMessage.Content) - { - dataStream = new MemoryStream(); + using (HttpContent content = responseMessage.Content) + { + dataStream = new MemoryStream(); - await content.CopyToAsync(dataStream).ConfigureAwait(false); + await content.CopyToAsync(dataStream).ConfigureAwait(false); - // Ensure we point the stream at the origin. - dataStream.Position = 0; - } + // Ensure we point the stream at the origin. + dataStream.Position = 0; + } #else // WINDOWS_UWP IBuffer dataBuffer = null; using (IHttpContent messageContent = responseMessage.Content) @@ -138,46 +177,10 @@ public static async Task CreateAsync( { // Do nothing if we fail to get additional error details from the response body. } - return error; - } - /// - /// Initializes a new instance of the class. - /// - /// Http status code. - /// Reason for exception. - /// Request URI which threw the exception. - /// Optional message. - /// Optional inner exception. - public DevicePortalException( - HttpStatusCode statusCode, - string reason, - Uri requestUri = null, - string message = "", - Exception innerException = null) : base( - message, - innerException) - { - this.StatusCode = statusCode; - this.Reason = reason; - this.RequestUri = requestUri; + return error; } - /// - /// Gets the HTTP Status code. - /// - public HttpStatusCode StatusCode { get; private set; } - - /// - /// Gets a reason for the exception. - /// - public string Reason { get; private set; } - - /// - /// Gets the request URI that threw the exception. - /// - public Uri RequestUri { get; private set; } - #if !WINDOWS_UWP /// /// Get object data override diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HoloLens/HolographicPerception.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HoloLens/HolographicPerception.cs index e60b04bd..81849983 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HoloLens/HolographicPerception.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HoloLens/HolographicPerception.cs @@ -75,7 +75,7 @@ public async Task CreatePerceptionSimulationControlStreamAsync(Simulatio throw new NotSupportedException("This method is only supported on HoloLens."); } - if (!(await VerifySimulationControlModeAsync(SimulationControlMode.Simulation))) + if (!(await this.VerifySimulationControlModeAsync(SimulationControlMode.Simulation))) { throw new InvalidOperationException("The simulation control mode on the target HoloLens must be 'Simulation'."); } @@ -84,7 +84,7 @@ public async Task CreatePerceptionSimulationControlStreamAsync(Simulatio "priority={0}", (int)priority); - PerceptionSimulationControlStreamId controlStreamId = await this.GetAsync( + PerceptionSimulationControlStreamId controlStreamId = await this.GetAsync( HolographicSimulationStreamApi, payload); @@ -104,7 +104,7 @@ public async Task DeletePerceptionSimulationControlStreamAsync(string streamId) throw new NotSupportedException("This method is only supported on HoloLens."); } - if (!(await VerifySimulationControlModeAsync(SimulationControlMode.Simulation))) + if (!(await this.VerifySimulationControlModeAsync(SimulationControlMode.Simulation))) { throw new InvalidOperationException("The simulation control mode on the target HoloLens must be 'Simulation'."); } @@ -161,7 +161,7 @@ public async Task SetPerceptionSimulationControlModeAsync(SimulationControlMode private async Task VerifySimulationControlModeAsync(SimulationControlMode expectedMode) { SimulationControlMode simMode = await this.GetPerceptionSimulationControlModeAsync(); - return (simMode == expectedMode); + return simMode == expectedMode; } #region Data contract diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/HttpHeadersHelper.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/HttpHeadersHelper.cs index fa796da4..11d9527b 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/HttpHeadersHelper.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/HttpHeadersHelper.cs @@ -31,6 +31,11 @@ public partial class DevicePortal /// private static readonly string ContentTypeHeaderName = "Content-Type"; + /// + /// Header name for a CSRF-Token. + /// + private static readonly string CsrfTokenName = "CSRF-Token"; + /// /// Header name for a User-Agent. /// @@ -41,6 +46,39 @@ public partial class DevicePortal /// private static readonly string UserAgentValue = "WindowsDevicePortalWrapper"; + /// + /// CSRF token retrieved by GET calls and used on subsequent POST/DELETE/PUT calls. + /// This token is intended to prevent a security vulnerability from cross site forgery. + /// + private string csrfToken = string.Empty; + + /// + /// Applies the CSRF token to the HTTP client. + /// + /// The HTTP client on which to have the header set. + /// The HTTP method (ex: POST) that will be called on the client. + private void ApplyCSRFHeader( + HttpClient client, + HttpMethods method) + { + string headerName = "X-" + CsrfTokenName; + string headerValue = this.csrfToken; + + if (string.Compare(method.ToString(), "get", true) == 0) + { + headerName = CsrfTokenName; + headerValue = string.IsNullOrEmpty(this.csrfToken) ? "Fetch" : headerValue; + } + +#if WINDOWS_UWP + HttpRequestHeaderCollection headers = client.DefaultRequestHeaders; +#else + HttpRequestHeaders headers = client.DefaultRequestHeaders; +#endif // WINDOWS_UWP + + headers.Add(headerName, headerValue); + } + /// /// Applies any needed headers to the HTTP client. /// @@ -51,6 +89,7 @@ private void ApplyHttpHeaders( HttpMethods method) { this.ApplyUserAgentHeader(client); + this.ApplyCSRFHeader(client, method); } /// @@ -76,5 +115,38 @@ private void ApplyUserAgentHeader(HttpClient client) headers.Add(UserAgentName, userAgentValue); } + + /// + /// Retrieves the CSRF token from the HTTP response and stores it. + /// + /// The HTTP response from which to retrieve the header. + private void RetrieveCsrfToken(HttpResponseMessage response) + { + // If the response sets a CSRF token, store that for future requests. +#if WINDOWS_UWP + string cookie; + if (response.Headers.TryGetValue("Set-Cookie", out cookie)) + { + string csrfTokenNameWithEquals = CsrfTokenName + "="; + if (cookie.StartsWith(csrfTokenNameWithEquals)) + { + this.csrfToken = cookie.Substring(csrfTokenNameWithEquals.Length); + } + } +#else + IEnumerable cookies; + if (response.Headers.TryGetValues("Set-Cookie", out cookies)) + { + foreach (string cookie in cookies) + { + string csrfTokenNameWithEquals = CsrfTokenName + "="; + if (cookie.StartsWith(csrfTokenNameWithEquals)) + { + this.csrfToken = cookie.Substring(csrfTokenNameWithEquals.Length); + } + } + } +#endif + } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs index 05f16a7b..1a49e754 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs @@ -158,7 +158,6 @@ private void ConvertStreamToMessage(Stream stream) UseSimpleDictionaryFormat = true }; DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), settings); - T message = (T)serializer.ReadObject(stream); diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/IoTOnboarding.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/IoTOnboarding.cs index 168a704d..bfd6e21b 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/IoTOnboarding.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/IoTOnboarding.cs @@ -63,8 +63,8 @@ await this.PostAsync( /// /// AllJoyn Status. /// AllJoyn Description. - /// AllJoyn Manufacturer. - /// AllJoyn Number. + /// AllJoyn Manufacturer. + /// AllJoyn Number. /// Task tracking completion of the REST call. public async Task SetAllJoynSettingsAsync(string allJoynStatus, string allJoynDescription, string allJoynManufacturer, string allJoynModelNumber) { diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/Limpet.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/Limpet.cs index 9ed01efa..25f24433 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/Limpet.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/Limpet.cs @@ -63,6 +63,7 @@ public async Task GetTpmAcpiTablesInfoAsync() /// /// Gets TPM Logical Device Settings information. /// + /// The device id /// String containing the TPM Logical Device Settings information. public async Task GetTpmLogicalDeviceSettingsInfoAsync(int logicalDeviceId) { @@ -94,6 +95,8 @@ public async Task ResetTpmLogicalDeviceSettingsInfoAsync(int logicalDeviceId) /// /// Gets TPM Azure Token information. /// + /// The device id + /// Validity of the token /// String containing the TPM Azure Token information. public async Task GetTpmAzureTokenInfoAsync(int logicalDeviceId, string validity) { diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/ProcessManagement.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/ProcessManagement.cs index 648d7a3c..569699db 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/ProcessManagement.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/ProcessManagement.cs @@ -29,8 +29,8 @@ public partial class DevicePortal /// /// Runs the command. /// - /// Command. - /// /// Run As Default Account. + /// The command. + /// Run As Default Account. /// Task tracking completion of the REST call. public async Task RunCommandAsync(string command, string runAsDefaultAccount) { @@ -43,7 +43,7 @@ await this.PostAsync( /// /// Command Without Output. /// Run As Default Account. - /// Timeout. + /// The timeout value. /// String containing the output after the command is executed. public async Task RunCommandWithoutOutputAsync(string commandWithoutOutput, string runAsDefaultAccount, string timeout) { @@ -60,12 +60,11 @@ public async Task RunCommandWithoutOutputAsync(string comm public class RunCommandOutputInfo { /// - /// Returns the output for the command executed. + /// Gets the output for the command executed. /// [DataMember(Name = "output")] - public string output { get; private set; } + public string Output { get; private set; } } #endregion // Data contract - } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/WindowsUpdate.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/WindowsUpdate.cs index 8ac09c71..0b9a3d76 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/WindowsUpdate.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/WindowsUpdate.cs @@ -107,13 +107,13 @@ public class UpdateInstallTimeInfo public string RebootScheduledTimeAsString { get; private set; } /// - /// Gets the time when a reboot is scheduled in DateTime format. + /// Gets the time when a reboot is scheduled in DateTime format. /// public DateTime RebootScheduledTime { get { - return DateTime.Parse(RebootScheduledTimeAsString); + return DateTime.Parse(this.RebootScheduledTimeAsString); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Core/AppDeployment.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Core/AppDeployment.cs index b40c2767..759ed2da 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Core/AppDeployment.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Core/AppDeployment.cs @@ -125,12 +125,11 @@ public async Task GetInstallStatusAsync() /// InstallApplication sends ApplicationInstallStatus events to indicate the current progress in the installation process. /// Some applications may opt to not register for the AppInstallStatus event and await on InstallApplication. /// Task for tracking completion of install initialization. - public async Task InstallApplicationAsync( string appName, StorageFile packageFile, List dependencyFiles, - StorageFile certificateFile= null, + StorageFile certificateFile = null, short stateCheckIntervalMs = 500, short timeoutInMinutes = 15, bool uninstallPreviousVersion = true) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RequestHelpers.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RequestHelpers.cs index a02a98fa..4b714f95 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RequestHelpers.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RequestHelpers.cs @@ -24,6 +24,7 @@ public partial class DevicePortal /// /// The file to be copied. /// The stream to which the file will be copied. + /// The async task. private static async Task CopyFileToRequestStream( StorageFile file, Stream stream) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestDelete.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestDelete.cs index 8ae60a44..08e16f73 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestDelete.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestDelete.cs @@ -52,6 +52,8 @@ private async Task DeleteAsync(Uri uri) throw await DevicePortalException.CreateAsync(response); } + this.RetrieveCsrfToken(response); + if (response.Content != null) { using (IHttpContent messageContent = response.Content) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestGet.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestGet.cs index 10e3952b..394b93cc 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestGet.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestGet.cs @@ -52,6 +52,8 @@ private async Task GetAsync(Uri uri) throw await DevicePortalException.CreateAsync(response); } + this.RetrieveCsrfToken(response); + using (IHttpContent messageContent = response.Content) { dataBuffer = await messageContent.ReadAsBufferAsync(); diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPost.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPost.cs index b8eabb84..291422c1 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPost.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPost.cs @@ -65,6 +65,8 @@ private async Task PostAsync( throw await DevicePortalException.CreateAsync(response); } + this.RetrieveCsrfToken(response); + if (response.Content != null) { using (IHttpContent messageContent = response.Content) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPut.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPut.cs index 18661ab6..6b785769 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPut.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPut.cs @@ -57,6 +57,8 @@ private async Task PutAsync( throw await DevicePortalException.CreateAsync(response); } + this.RetrieveCsrfToken(response); + if (response.Content != null) { using (IHttpContent messageContent = response.Content) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs index 36e77b04..64e26ff1 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs @@ -20,14 +20,14 @@ namespace Microsoft.Tools.WindowsDevicePortal internal partial class WebSocket { /// - /// The that is being wrapped. + /// The websocket connection has closed after the request was fulfilled. /// - private MessageWebSocket websocket = null; + private const ushort WebSocketCloseStatusNormalClosure = 1000; /// - /// The websocket connection has closed after the request was fulfilled. + /// The that is being wrapped. /// - private UInt16 WebSocketCloseStatus_NormalClosure = 1000; + private MessageWebSocket websocket = null; /// /// Initializes a new instance of the class. @@ -89,7 +89,7 @@ private async Task CloseInternalAsync() { await Task.Run(() => { - this.websocket.Close(WebSocketCloseStatus_NormalClosure, "Closed due to user request."); + this.websocket.Close(WebSocketCloseStatusNormalClosure, "Closed due to user request."); this.websocket.Dispose(); this.websocket = null; this.IsConnected = false; diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/DefaultDevicePortalConnection.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/DefaultDevicePortalConnection.cs index 07efdb16..a04d0b53 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/DefaultDevicePortalConnection.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/DefaultDevicePortalConnection.cs @@ -56,7 +56,6 @@ public DefaultDevicePortalConnection( { this.Connection = new Uri(address); - if (!string.IsNullOrEmpty(userName) && password != null && password.Length > 0) @@ -91,7 +90,8 @@ public Uri WebSocketConnection string scheme = this.Connection.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) ? "wss" : "ws"; return new Uri( - string.Format("{0}://{1}", + string.Format( + "{0}://{1}", scheme, this.Connection.Authority)); } @@ -178,7 +178,8 @@ public void UpdateConnection( string address = addressInfo.Address; if (preservePort) { - address = string.Format("{0}:{1}", + address = string.Format( + "{0}:{1}", address, this.Connection.Port); } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestDelete.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestDelete.cs index 428c65c5..c04905d4 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestDelete.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestDelete.cs @@ -42,6 +42,8 @@ private async Task DeleteAsync(Uri uri) throw await DevicePortalException.CreateAsync(response); } + this.RetrieveCsrfToken(response); + if (response.Content != null) { using (HttpContent content = response.Content) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestGet.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestGet.cs index e6c0d170..d4b29e43 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestGet.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestGet.cs @@ -42,6 +42,8 @@ private async Task GetAsync( throw await DevicePortalException.CreateAsync(response); } + this.RetrieveCsrfToken(response); + using (HttpContent content = response.Content) { dataStream = new MemoryStream(); diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPost.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPost.cs index 13f5a015..ad0679ca 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPost.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPost.cs @@ -55,6 +55,8 @@ private async Task PostAsync( throw await DevicePortalException.CreateAsync(response); } + this.RetrieveCsrfToken(response); + if (response.Content != null) { using (HttpContent responseContent = response.Content) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPut.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPut.cs index 7097c753..ee58a4e8 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPut.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPut.cs @@ -46,6 +46,8 @@ private async Task PutAsync( throw await DevicePortalException.CreateAsync(response); } + this.RetrieveCsrfToken(response); + if (response.Content != null) { using (HttpContent content = response.Content)