allow again the push

This commit is contained in:
Paul Schneider 2026-07-05 21:44:49 +01:00
commit a78847dbc9
5 changed files with 94 additions and 7 deletions

View file

@ -18,6 +18,8 @@ namespace Isn
source = Program.Settings.Sources.First(s=>s.Value.Alias==source).Key; source = Program.Settings.Sources.First(s=>s.Value.Alias==source).Key;
if (source==null) throw new InvalidOperationException("source is invalid"); if (source==null) throw new InvalidOperationException("source is invalid");
var resolvedApiKey = string.IsNullOrWhiteSpace(apikey) ? ResolveSourceApiKey(source) : apikey;
var resources = SourceHelpers.GetServerResources(source); var resources = SourceHelpers.GetServerResources(source);
if (resources.Resources == null) if (resources.Resources == null)
throw new InvalidOperationException("source gave no resource"); throw new InvalidOperationException("source gave no resource");
@ -38,7 +40,7 @@ namespace Isn
try try
{ {
Console.WriteLine("Connecting to "+ pubRes.Id); 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) catch (HttpRequestException hrex)
{ {
@ -64,5 +66,25 @@ namespace Isn
return report; 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;
}
} }
} }

View file

@ -36,7 +36,7 @@ namespace isnd.Controllers
var files = new List<string>(); var files = new List<string>();
ViewData["files"] = files; ViewData["files"] = files;
var clearkey = ApiKeyProtector.UnprotectWithFallback(protector, apiKey); var clearkey = ApiKeyProtector.TryUnprotectKey(protector, apiKey);
var apikey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearkey); var apikey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearkey);
if (apikey == null) if (apikey == null)
{ {

View file

@ -9,24 +9,56 @@ namespace isnd.Helpers
{ {
private const byte LegacyDelta = 145; 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)) if (string.IsNullOrWhiteSpace(protectedValue))
{ {
return 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 try
{ {
return protector.Unprotect(protectedValue); unprotected = protector.Unprotect(protectedValue);
return true;
} }
catch (CryptographicException) catch (CryptographicException)
{ {
return UnprotectLegacy(protectedValue); unprotected = null;
return false;
} }
catch (FormatException) 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;
} }
} }

View file

@ -21,6 +21,8 @@ namespace Isn.tests
private readonly string configDirectory; private readonly string configDirectory;
private readonly string originalConfigDirectory; private readonly string originalConfigDirectory;
public string LastApiKeyHeader { get; private set; }
public LocalSourceFixture() public LocalSourceFixture()
{ {
var port = GetFreePort(); var port = GetFreePort();
@ -47,6 +49,11 @@ namespace Isn.tests
break; break;
} }
if (context.Request.HttpMethod == "PUT")
{
LastApiKeyHeader = context.Request.Headers["X-NuGet-ApiKey"];
}
var indexJson = JsonConvert.SerializeObject(new ApiIndexViewModel(prefix + "index.json") var indexJson = JsonConvert.SerializeObject(new ApiIndexViewModel(prefix + "index.json")
{ {
Version = "3.0.0", Version = "3.0.0",
@ -206,6 +213,22 @@ namespace Isn.tests
Assert.Single(report); 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] [Fact]
public void GetServerResourcesUsingHttpClientAsyncTest() public void GetServerResourcesUsingHttpClientAsyncTest()
{ {

View file

@ -58,11 +58,21 @@ namespace isnd.host.tests
const string clearValue = "legacy-api-key"; const string clearValue = "legacy-api-key";
var legacyProtectedValue = new Isn.DefaultDataProtector().Protect(clearValue); 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); 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] [Fact]
void TestDropUser() void TestDropUser()
{ {