From 8a73de245643eeddb423505f11dd3e841bb78ec4 Mon Sep 17 00:00:00 2001 From: vagisha Date: Mon, 3 Feb 2025 16:42:20 -0800 Subject: [PATCH 1/2] Fix "Open from Panorama" with anonymous server (#3355) - Renamed WebClientWithCredentials to LabkeySessionWebClient - Add the Authorization header in LabkeySessionWebClient only if a username and password is provided. Otherwise, the request will fail. Prior to LK 24.11 a call that failed authentication would proceed as an unauthenticated user. - Added test in PanoramaClientDownloadTest for downloading a document from PanoramaWeb an a guest user. --- .../Shared/PanoramaClient/PanoramaClient.cs | 18 ++--- .../PanoramaClient/PanoramaFilePicker.cs | 3 +- .../PanoramaClient/PanoramaFolderBrowser.cs | 2 +- .../Shared/PanoramaClient/PanoramaUtil.cs | 45 ++++++------ .../Shared/PanoramaClient/RequestHelper.cs | 4 +- .../AutoQC/AutoQC/PanoramaSettings.cs | 4 +- .../AutoQC/AutoQCTest/PanoramaTest.cs | 4 +- .../PanoramaClientDownloadTest.cs | 68 ++++++++++++++++++- 8 files changed, 103 insertions(+), 45 deletions(-) diff --git a/pwiz_tools/Shared/PanoramaClient/PanoramaClient.cs b/pwiz_tools/Shared/PanoramaClient/PanoramaClient.cs index 72c393752c..4700d6e700 100644 --- a/pwiz_tools/Shared/PanoramaClient/PanoramaClient.cs +++ b/pwiz_tools/Shared/PanoramaClient/PanoramaClient.cs @@ -773,7 +773,7 @@ public override PanoramaServer EnsureLogin(PanoramaServer pServer) public override void DownloadFile(string fileUrl, string fileName, long fileSize, string realName, IProgressMonitor pm, IProgressStatus progressStatus) { // TODO: Change this to use IRequestHelper - using var wc = new WebClientWithCredentials(ServerUri, Username, Password); + using var wc = new LabkeySessionWebClient(new PanoramaServer(ServerUri, Username, Password)); wc.DownloadProgressChanged += (s, e) => { var progressPercent = e.ProgressPercentage > 0 ? e.ProgressPercentage : -1; @@ -818,9 +818,10 @@ public override void DownloadFile(string fileUrl, string fileName, long fileSize public override IRequestHelper GetRequestHelper(bool forPublish = false) { + var panoramaServer = new PanoramaServer(ServerUri, Username, Password); var webClient = forPublish - ? new NonStreamBufferingWebClient(ServerUri, Username, Password) - : new WebClientWithCredentials(ServerUri, Username, Password); + ? new NonStreamBufferingWebClient(panoramaServer) + : new LabkeySessionWebClient(panoramaServer); return new PanoramaRequestHelper(webClient); } @@ -839,7 +840,7 @@ public string DownloadStringAsync(Uri queryUri, CancellationToken cancelToken) string data = null; Exception error = null; - using (var webClient = GetWebClientForServer(new PanoramaServer(ServerUri, Username, Password))) + using (var webClient = new LabkeySessionWebClient(new PanoramaServer(ServerUri, Username, Password))) { bool finishedDownloading = false; webClient.DownloadStringAsync(queryUri); @@ -861,15 +862,6 @@ public string DownloadStringAsync(Uri queryUri, CancellationToken cancelToken) throw error; return data; } - - private static WebClient GetWebClientForServer(PanoramaServer server) - { - return server.HasUserAccount() - ? new WebClientWithCredentials(server.URI, server.Username, server.Password) - : new UTF8WebClient();// Use anonymous client if we were not given a username and password. Otherwise, request will fail. - // Prior to LK 24.11 a call that failed authentication would proceed as an unauthenticated user - - } } /// diff --git a/pwiz_tools/Shared/PanoramaClient/PanoramaFilePicker.cs b/pwiz_tools/Shared/PanoramaClient/PanoramaFilePicker.cs index 27f712f448..a53e6f4b17 100644 --- a/pwiz_tools/Shared/PanoramaClient/PanoramaFilePicker.cs +++ b/pwiz_tools/Shared/PanoramaClient/PanoramaFilePicker.cs @@ -123,8 +123,7 @@ private static Uri BuildQuery(string server, string folderPath, string queryName /// private JToken GetJson(Uri queryUri) { - using (var requestHelper = new PanoramaRequestHelper(new WebClientWithCredentials(queryUri, FolderBrowser.GetActiveServer().Username, - FolderBrowser.GetActiveServer().Password))) + using (var requestHelper = new PanoramaRequestHelper(new LabkeySessionWebClient(FolderBrowser.GetActiveServer()))) { JToken json = requestHelper.Get(queryUri); return json; diff --git a/pwiz_tools/Shared/PanoramaClient/PanoramaFolderBrowser.cs b/pwiz_tools/Shared/PanoramaClient/PanoramaFolderBrowser.cs index 4d331aef9e..07ccfa74f5 100644 --- a/pwiz_tools/Shared/PanoramaClient/PanoramaFolderBrowser.cs +++ b/pwiz_tools/Shared/PanoramaClient/PanoramaFolderBrowser.cs @@ -541,7 +541,7 @@ private void AddWebDavFolders(TreeNode node) try { query = new Uri(string.Concat(folderInfo.Server.URI, PanoramaUtil.WEBDAV, folderInfo.FolderPath, "?method=json")); - using var requestHelper = new PanoramaRequestHelper(new WebClientWithCredentials(query, folderInfo.Server.Username, folderInfo.Server.Password)); + using var requestHelper = new PanoramaRequestHelper(new LabkeySessionWebClient(folderInfo.Server)); JToken json = requestHelper.Get(query); if ((int)json[@"fileCount"] != 0) { diff --git a/pwiz_tools/Shared/PanoramaClient/PanoramaUtil.cs b/pwiz_tools/Shared/PanoramaClient/PanoramaUtil.cs index 13f4a30901..effcdb2c3a 100644 --- a/pwiz_tools/Shared/PanoramaClient/PanoramaUtil.cs +++ b/pwiz_tools/Shared/PanoramaClient/PanoramaUtil.cs @@ -642,37 +642,43 @@ public UTF8WebClient() } } - public class WebClientWithCredentials : UTF8WebClient + public class LabkeySessionWebClient : UTF8WebClient { private CookieContainer _cookies = new CookieContainer(); private string _csrfToken; private readonly Uri _serverUri; - private static string LABKEY_CSRF = @"X-LABKEY-CSRF"; + private const string LABKEY_CSRF = @"X-LABKEY-CSRF"; - public WebClientWithCredentials(Uri serverUri, string username, string password) + public LabkeySessionWebClient(PanoramaServer server) { - // Add the Authorization header - Headers.Add(HttpRequestHeader.Authorization, PanoramaServer.GetBasicAuthHeader(username, password)); - _serverUri = serverUri; + if (server == null) + { + throw new ArgumentNullException(nameof(server)); + } + // Add the Authorization header only if a username and password is provided. Otherwise, the request will fail. + // Prior to LK 24.11 a call that failed authentication would proceed as an unauthenticated user + if (server.HasUserAccount()) + { + Headers.Add(HttpRequestHeader.Authorization, server.AuthHeader); + } + + _serverUri = server.URI; } protected override WebRequest GetWebRequest(Uri address) { var request = base.GetWebRequest(address); - - var httpWebRequest = request as HttpWebRequest; - if (httpWebRequest != null) + + if (request is HttpWebRequest httpWebRequest) { httpWebRequest.CookieContainer = _cookies; - if (request.Method == PanoramaUtil.FORM_POST) + if (request.Method == PanoramaUtil.FORM_POST && !string.IsNullOrEmpty(_csrfToken)) { - if (!string.IsNullOrEmpty(_csrfToken)) - { - // All POST requests to LabKey Server will be checked for a CSRF token - request.Headers.Add(LABKEY_CSRF, _csrfToken); - } + // All POST requests to LabKey Server will be checked for a CSRF token + request.Headers.Add(LABKEY_CSRF, _csrfToken); + } } return request; @@ -681,8 +687,7 @@ protected override WebRequest GetWebRequest(Uri address) protected override WebResponse GetWebResponse(WebRequest request) { var response = base.GetWebResponse(request); - var httpResponse = response as HttpWebResponse; - if (httpResponse != null) + if (response is HttpWebResponse httpResponse) { GetCsrfToken(httpResponse, request.RequestUri); } @@ -742,10 +747,10 @@ public void ClearCsrfToken() } } - public class NonStreamBufferingWebClient : WebClientWithCredentials + public class NonStreamBufferingWebClient : LabkeySessionWebClient { - public NonStreamBufferingWebClient(Uri serverUri, string username, string password) - : base(serverUri, username, password) + public NonStreamBufferingWebClient(PanoramaServer server) + : base(server) { } diff --git a/pwiz_tools/Shared/PanoramaClient/RequestHelper.cs b/pwiz_tools/Shared/PanoramaClient/RequestHelper.cs index 197fb451fa..9fdd34f444 100644 --- a/pwiz_tools/Shared/PanoramaClient/RequestHelper.cs +++ b/pwiz_tools/Shared/PanoramaClient/RequestHelper.cs @@ -195,9 +195,9 @@ private JObject ParseResponse(string response, Uri uri, string messageOnError) public class PanoramaRequestHelper : AbstractRequestHelper { - private readonly WebClientWithCredentials _client; + private readonly LabkeySessionWebClient _client; - public PanoramaRequestHelper(WebClientWithCredentials webClient) + public PanoramaRequestHelper(LabkeySessionWebClient webClient) { _client = webClient; } diff --git a/pwiz_tools/Skyline/Executables/AutoQC/AutoQC/PanoramaSettings.cs b/pwiz_tools/Skyline/Executables/AutoQC/AutoQC/PanoramaSettings.cs index 29e82e6d2c..252572e131 100644 --- a/pwiz_tools/Skyline/Executables/AutoQC/AutoQC/PanoramaSettings.cs +++ b/pwiz_tools/Skyline/Executables/AutoQC/AutoQC/PanoramaSettings.cs @@ -329,8 +329,8 @@ public void PingPanorama() public void Init() { - _requestHelper = new PanoramaRequestHelper(new WebClientWithCredentials(_panoramaSettings.PanoramaServerUri, - _panoramaSettings.PanoramaUserEmail, _panoramaSettings.PanoramaPassword)); + _requestHelper = new PanoramaRequestHelper(new LabkeySessionWebClient(new PanoramaServer(_panoramaSettings.PanoramaServerUri, + _panoramaSettings.PanoramaUserEmail, _panoramaSettings.PanoramaPassword))); _timer = new Timer(e => { PingPanoramaServer(); }); _timer.Change(TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(5)); // Ping Panorama every 5 minutes. } diff --git a/pwiz_tools/Skyline/Executables/AutoQC/AutoQCTest/PanoramaTest.cs b/pwiz_tools/Skyline/Executables/AutoQC/AutoQCTest/PanoramaTest.cs index 62b45be2fa..6e2f5736e4 100644 --- a/pwiz_tools/Skyline/Executables/AutoQC/AutoQCTest/PanoramaTest.cs +++ b/pwiz_tools/Skyline/Executables/AutoQC/AutoQCTest/PanoramaTest.cs @@ -113,8 +113,8 @@ private async Task SuccessfulPanoramaUpload(string uniqueFolder) var labKeyQuery = PanoramaUtil.CallNewInterface(panoramaServerUri, "query", $"{uniqueFolder}", "selectRows", "schemaName=targetedms&queryName=runs", true); var requestHelper = - new PanoramaRequestHelper(new WebClientWithCredentials(panoramaServerUri, TestUtils.PANORAMAWEB_USER, - TestUtils.GetPanoramaWebPassword())); + new PanoramaRequestHelper(new LabkeySessionWebClient(new PanoramaServer(panoramaServerUri, TestUtils.PANORAMAWEB_USER, + TestUtils.GetPanoramaWebPassword()))); var startTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; var x = startTime; while (x < startTime + TIMEOUT_80SEC) diff --git a/pwiz_tools/Skyline/TestConnected/PanoramaClientDownloadTest.cs b/pwiz_tools/Skyline/TestConnected/PanoramaClientDownloadTest.cs index b98f47370f..f93eb58560 100644 --- a/pwiz_tools/Skyline/TestConnected/PanoramaClientDownloadTest.cs +++ b/pwiz_tools/Skyline/TestConnected/PanoramaClientDownloadTest.cs @@ -26,6 +26,7 @@ using pwiz.PanoramaClient; using pwiz.Skyline.Alerts; using pwiz.Skyline.Properties; +using pwiz.Skyline.SettingsUI; using pwiz.Skyline.ToolsUI; using pwiz.Skyline.Util; using pwiz.SkylineTestUtil; @@ -74,8 +75,11 @@ protected override void DoTest() // Test viewing webDav browser TestWebDav(); - // Test adding a new server to Panorama - Test with and without username and password + // Test adding a new server to Panorama TestAddServer(); + + // Test adding PanoramaWeb as an anonymous server, and downloading a document from Panorama Public. + TestWithAnonymousServer(); } private void AddPanoramaServers() @@ -239,7 +243,7 @@ private void TestRenamedFile() Assert.IsFalse(File.Exists(path)); } - //TODO: Test adding a new server to Panorama - Test with and without username and password + //Test adding a new Panorama server. private void TestAddServer() { var path = TEST_FOLDER; @@ -258,10 +262,68 @@ private void TestAddServer() var remoteDlg = ShowDialog(editItem.OkDialog); if (Settings.Default.ServerList != null) Assert.AreEqual(1, Settings.Default.ServerList.Count); - RunUI(() => Assert.IsTrue(remoteDlg.IsLoaded)); + RunUI(() => + { + Assert.IsTrue(remoteDlg.IsLoaded); + Assert.IsTrue(remoteDlg.FolderBrowser.SelectNode(TEST_FOLDER), "Unable to select folder '{0}'", TEST_FOLDER); + Assert.IsTrue(remoteDlg.FolderBrowser.SelectNode(PANORAMA_FOLDER), "Unable to select folder '{0}'", PANORAMA_FOLDER); + }); + OkDialog(remoteDlg, remoteDlg.Close); } + private void TestWithAnonymousServer() + { + var toolsOptionsDlg = ShowDialog(() => SkylineWindow.ShowToolOptionsUI(ToolOptionsUI.TABS.Panorama)); + + // Clear the Panorama server list + var editServerListDlg = ShowDialog, Server>>(toolsOptionsDlg.EditServers); + RunUI(editServerListDlg.ResetList); + + // Add PanoramaWeb as an anonymous server + var editServerDlg = ShowDialog(editServerListDlg.AddItem); + RunUI(() => + { + editServerDlg.URL = PANORAMA_WEB; + editServerDlg.AnonymousServer = true; + }); + OkDialog(editServerDlg, editServerDlg.OkDialog); + OkDialog(editServerListDlg, editServerListDlg.OkDialog); + OkDialog(toolsOptionsDlg, toolsOptionsDlg.OkDialog); + + Assert.IsNotNull(Settings.Default.ServerList); + Assert.AreEqual(1, Settings.Default.ServerList.Count); + + const string panoramaPublicFolderPath = "Panorama Public/2018/MacLean - Baker IMS"; + // Document in the "Panorama Public/2018/MacLean - Baker IMS" folder on https://panoramaweb.org that we will download. + const string skyDocName = "BSA-Training_2017-09-21_17-59-13.sky.zip"; + var downloadFilePath = TestContext.GetTestResultsPath(skyDocName); + FileEx.SafeDelete(downloadFilePath, true); + Assert.IsFalse(File.Exists(downloadFilePath)); + + var panoramaFilePickerDlg = ShowDialog(() => SkylineWindow.OpenFromPanorama(downloadFilePath)); + RunUI(() => + { + Assert.IsTrue(panoramaFilePickerDlg.IsLoaded); + Assert.IsFalse(panoramaFilePickerDlg.FolderBrowser.SelectNode(TEST_FOLDER), + "Guest user should not be able to see the private folder '{0}'.", TEST_FOLDER); + + foreach (var folder in panoramaPublicFolderPath.Split('/')) + { + Assert.IsTrue(panoramaFilePickerDlg.FolderBrowser.SelectNode(folder), + "Guest user should be able to see folder '{0}' in the folder path '{1}'.", folder, panoramaPublicFolderPath); + } + }); + + var doc = SkylineWindow.Document; + OkDialog(panoramaFilePickerDlg, () => Assert.IsTrue(panoramaFilePickerDlg.ClickFile(skyDocName), "Unable to select Skyline document {0}", skyDocName)); + WaitForCondition(() => File.Exists(downloadFilePath)); + var docLoaded = WaitForDocumentChangeLoaded(doc); + AssertEx.IsDocumentState(docLoaded, null, 1, 34, 38, 404); + FileEx.SafeDelete(downloadFilePath, true); + Assert.IsFalse(File.Exists(downloadFilePath)); + } + // Test viewing webDav browser private void TestWebDav() { From 69e37b6003a0123de038bb8a0e537309c8d96389 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 5 Feb 2025 11:58:37 -0800 Subject: [PATCH 2/2] Avoid a null reference in graph update in a race condition with an action that removes all chromatograms (#3357) --- pwiz_tools/Skyline/Controls/Graphs/GraphFullScan.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwiz_tools/Skyline/Controls/Graphs/GraphFullScan.cs b/pwiz_tools/Skyline/Controls/Graphs/GraphFullScan.cs index 7bbffe8a2a..240646adf7 100644 --- a/pwiz_tools/Skyline/Controls/Graphs/GraphFullScan.cs +++ b/pwiz_tools/Skyline/Controls/Graphs/GraphFullScan.cs @@ -614,7 +614,7 @@ private void PopulateProperties() if (_documentContainer is SkylineWindow stateProvider) { - var chromSet = stateProvider.DocumentUI.Settings.MeasuredResults.Chromatograms.FirstOrDefault( + var chromSet = stateProvider.DocumentUI.Settings.MeasuredResults?.Chromatograms.FirstOrDefault( chrom => chrom.ContainsFile(_msDataFileScanHelper.ScanProvider.DataFilePath)); spectrumProperties.ReplicateName = chromSet?.Name; if (_peaks?.Length > 0)