From a78847dbc909464087db02e149f4f35dd70bb983 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sun, 5 Jul 2026 21:44:49 +0100 Subject: [PATCH] allow again the push --- src/isn/commands/PushCommand.cs | 24 ++++++++++- .../Packages/PackagesController.Put.cs | 2 +- src/isnd/Helpers/ApiKeyProtector.cs | 40 +++++++++++++++++-- test/isn.tests/PushTest.cs | 23 +++++++++++ test/isnd.tests/UnitTestWebHost.cs | 12 +++++- 5 files changed, 94 insertions(+), 7 deletions(-) diff --git a/src/isn/commands/PushCommand.cs b/src/isn/commands/PushCommand.cs index 0f6f303..6820814 100644 --- a/src/isn/commands/PushCommand.cs +++ b/src/isn/commands/PushCommand.cs @@ -18,6 +18,8 @@ namespace Isn source = Program.Settings.Sources.First(s=>s.Value.Alias==source).Key; if (source==null) throw new InvalidOperationException("source is invalid"); + var resolvedApiKey = string.IsNullOrWhiteSpace(apikey) ? ResolveSourceApiKey(source) : apikey; + var resources = SourceHelpers.GetServerResources(source); if (resources.Resources == null) throw new InvalidOperationException("source gave no resource"); @@ -38,7 +40,7 @@ namespace Isn try { Console.WriteLine("Connecting to "+ pubRes.Id); - return client.UploadFilesToServer(new Uri(pubRes.Id), fi, apikey); + return client.UploadFilesToServer(new Uri(pubRes.Id), fi, resolvedApiKey); } catch (HttpRequestException hrex) { @@ -64,5 +66,25 @@ namespace Isn return report; } } + + private static string ResolveSourceApiKey(string source) + { + if (string.IsNullOrWhiteSpace(source)) + { + return null; + } + + if (Program.Settings.Sources.Values.Any(s => s.Alias == source)) + { + source = Program.Settings.Sources.First(s => s.Value.Alias == source).Key; + } + + if (Program.Settings.Sources.TryGetValue(source, out var sourceSettings)) + { + return sourceSettings.GetClearApiKey(); + } + + return null; + } } } \ No newline at end of file diff --git a/src/isnd/Controllers/Packages/PackagesController.Put.cs b/src/isnd/Controllers/Packages/PackagesController.Put.cs index 18c3633..3084fa8 100644 --- a/src/isnd/Controllers/Packages/PackagesController.Put.cs +++ b/src/isnd/Controllers/Packages/PackagesController.Put.cs @@ -36,7 +36,7 @@ namespace isnd.Controllers var files = new List(); ViewData["files"] = files; - var clearkey = ApiKeyProtector.UnprotectWithFallback(protector, apiKey); + var clearkey = ApiKeyProtector.TryUnprotectKey(protector, apiKey); var apikey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearkey); if (apikey == null) { diff --git a/src/isnd/Helpers/ApiKeyProtector.cs b/src/isnd/Helpers/ApiKeyProtector.cs index 4a8062f..98f5e72 100644 --- a/src/isnd/Helpers/ApiKeyProtector.cs +++ b/src/isnd/Helpers/ApiKeyProtector.cs @@ -9,24 +9,56 @@ namespace isnd.Helpers { private const byte LegacyDelta = 145; - public static string UnprotectWithFallback(IDataProtector protector, string protectedValue) + public static string TryUnprotectKey(IDataProtector protector, string protectedValue) { if (string.IsNullOrWhiteSpace(protectedValue)) { return protectedValue; } + if (TryUnprotect(protector, protectedValue, out var unprotected)) + { + return unprotected; + } + + if (TryUnprotectLegacy(protectedValue, out unprotected)) + { + return unprotected; + } + + return protectedValue; + } + + private static bool TryUnprotect(IDataProtector protector, string protectedValue, out string unprotected) + { try { - return protector.Unprotect(protectedValue); + unprotected = protector.Unprotect(protectedValue); + return true; } catch (CryptographicException) { - return UnprotectLegacy(protectedValue); + unprotected = null; + return false; } catch (FormatException) { - return UnprotectLegacy(protectedValue); + unprotected = null; + return false; + } + } + + private static bool TryUnprotectLegacy(string protectedValue, out string unprotected) + { + try + { + unprotected = UnprotectLegacy(protectedValue); + return true; + } + catch (FormatException) + { + unprotected = null; + return false; } } diff --git a/test/isn.tests/PushTest.cs b/test/isn.tests/PushTest.cs index 2cc717f..1871ebf 100644 --- a/test/isn.tests/PushTest.cs +++ b/test/isn.tests/PushTest.cs @@ -21,6 +21,8 @@ namespace Isn.tests private readonly string configDirectory; private readonly string originalConfigDirectory; + public string LastApiKeyHeader { get; private set; } + public LocalSourceFixture() { var port = GetFreePort(); @@ -47,6 +49,11 @@ namespace Isn.tests break; } + if (context.Request.HttpMethod == "PUT") + { + LastApiKeyHeader = context.Request.Headers["X-NuGet-ApiKey"]; + } + var indexJson = JsonConvert.SerializeObject(new ApiIndexViewModel(prefix + "index.json") { Version = "3.0.0", @@ -206,6 +213,22 @@ namespace Isn.tests Assert.Single(report); } + [Fact] + public void PushUsesStoredApiKeyFromConfigWhenNoExplicitApiKeyIsProvided() + { + Program.LoadConfig(); + var clearApiKey = "stored-api-key"; + Program.Settings.Sources[fixture.SourceUrl].SetApiKey(clearApiKey); + Program.SaveConfig(); + Program.LoadConfig(); + + var report = Program.PushPkg(new[] { Path.Combine(AppContext.BaseDirectory, "dummy.nupkg") }); + + Assert.NotNull(report); + Assert.Single(report); + Assert.Equal(clearApiKey, fixture.LastApiKeyHeader); + } + [Fact] public void GetServerResourcesUsingHttpClientAsyncTest() { diff --git a/test/isnd.tests/UnitTestWebHost.cs b/test/isnd.tests/UnitTestWebHost.cs index 183e217..b89caae 100644 --- a/test/isnd.tests/UnitTestWebHost.cs +++ b/test/isnd.tests/UnitTestWebHost.cs @@ -58,11 +58,21 @@ namespace isnd.host.tests const string clearValue = "legacy-api-key"; var legacyProtectedValue = new Isn.DefaultDataProtector().Protect(clearValue); - var unprotected = ApiKeyProtector.UnprotectWithFallback(new ThrowingProtector(), legacyProtectedValue); + var unprotected = ApiKeyProtector.TryUnprotectKey(new ThrowingProtector(), legacyProtectedValue); Assert.Equal(clearValue, unprotected); } + [Fact] + public void ApiKeyProtectorReturnsRawValueWhenInputIsNotProtected() + { + const string rawApiKey = "CfDJ8AstXUEkxpJBrmoENwy__WNPxqcOwAuxyYgiU3Mebns5yxAT3VrC5vTuWTRSGZBFB7IGUyFkUYsp2nhRy64NqeUzLgOwEcU4FPOf-yaAtPeTMeStRd60bRh2ZYyQkQ3hrhW-lwpCLwYtNOnOyX2jayuzByF-4QfHIEhg6lbWenzf"; + + var result = ApiKeyProtector.TryUnprotectKey(new ThrowingProtector(), rawApiKey); + + Assert.Equal(rawApiKey, result); + } + [Fact] void TestDropUser() {