Compare commits
No commits in common. "a78847dbc909464087db02e149f4f35dd70bb983" and "cd72b7b9aae59d9eb7f67d61b7339dde4876fdb6" have entirely different histories.
a78847dbc9
...
cd72b7b9aa
8 changed files with 20 additions and 231 deletions
|
|
@ -3,14 +3,11 @@ set -e
|
||||||
|
|
||||||
# compiler tout
|
# compiler tout
|
||||||
dotnet build -c Release
|
dotnet build -c Release
|
||||||
dotnet publish -c Release src/isnd
|
dotnet publish -c Release -f netcoreapp2.1 src/isnd
|
||||||
dotnet publish -c Release src/isn
|
|
||||||
# MAJ du serveur
|
# MAJ du serveur
|
||||||
sudo systemctl stop isnd
|
sudo systemctl stop isnd
|
||||||
sudo cp -a src/isnd/bin/Release/net10.0/publish/* /srv/www/isnd
|
sudo cp -a src/isnd/bin/Release/netcoreapp2.1/publish/* /srv/www/isnd
|
||||||
sudo systemctl start isnd
|
sudo systemctl start isnd
|
||||||
# MAJ du client
|
# MAJ du client
|
||||||
sudo cp -a src/isn/bin/Release/net10.0/* /usr/local/lib/isn
|
sudo cp -a src/isn/bin/Release/netcoreapp2.1/* /usr/local/lib/isn
|
||||||
sudo chown -R root:root /usr/local/lib/isn
|
sudo chown -R root.root /usr/local/lib/isn
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Isn
|
||||||
|
|
||||||
public static void LoadConfig()
|
public static void LoadConfig()
|
||||||
{
|
{
|
||||||
FileInfo cfgSettingIf = new FileInfo(ConfigFileName);
|
FileInfo cfgSettingIf = new FileInfo(_configFileName);
|
||||||
if (cfgSettingIf.Exists)
|
if (cfgSettingIf.Exists)
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(cfgSettingIf.FullName);
|
var json = File.ReadAllText(cfgSettingIf.FullName);
|
||||||
|
|
@ -29,13 +29,12 @@ namespace Isn
|
||||||
{ "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
|
{ "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string ConfigDirectory =>
|
private static readonly string _configFileName =
|
||||||
Environment.GetEnvironmentVariable("ISN_CONFIG_DIR")
|
Path.Combine(
|
||||||
?? Path.Combine(
|
Path.Combine(Environment.GetFolderPath(
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
Environment.SpecialFolder.UserProfile), ".isn"),
|
||||||
".isn");
|
"config.json")
|
||||||
|
;
|
||||||
private static string ConfigFileName => Path.Combine(ConfigDirectory, "config.json");
|
|
||||||
|
|
||||||
public const string push = "push";
|
public const string push = "push";
|
||||||
static readonly OptionSet options = new OptionSet {
|
static readonly OptionSet options = new OptionSet {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@ 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");
|
||||||
|
|
@ -40,7 +38,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, resolvedApiKey);
|
return client.UploadFilesToServer(new Uri(pubRes.Id), fi, apikey);
|
||||||
}
|
}
|
||||||
catch (HttpRequestException hrex)
|
catch (HttpRequestException hrex)
|
||||||
{
|
{
|
||||||
|
|
@ -66,25 +64,5 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ namespace Isn
|
||||||
}
|
}
|
||||||
public static void SaveConfig()
|
public static void SaveConfig()
|
||||||
{
|
{
|
||||||
FileInfo cfgSettingIf = new FileInfo(ConfigFileName);
|
FileInfo cfgSettingIf = new FileInfo(_configFileName);
|
||||||
if (!cfgSettingIf.Directory.Exists) cfgSettingIf.Directory.Create();
|
if (!cfgSettingIf.Directory.Exists) cfgSettingIf.Directory.Create();
|
||||||
File.WriteAllText(
|
File.WriteAllText(
|
||||||
cfgSettingIf.FullName,
|
cfgSettingIf.FullName,
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,12 @@ namespace isnd.Controllers
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var clientVersionId = Request.Headers["X-NuGet-Client-Version"];
|
var clientVersionId = Request.Headers["X-NuGet-Client-Version"];
|
||||||
string apiKey = Request.Headers["X-NuGet-ApiKey"].FirstOrDefault() ?? string.Empty;
|
string apiKey = Request.Headers["X-NuGet-ApiKey"][0];
|
||||||
ViewData["versionId"] = typeof(PackagesController).Assembly.FullName;
|
ViewData["versionId"] = typeof(PackagesController).Assembly.FullName;
|
||||||
var files = new List<string>();
|
var files = new List<string>();
|
||||||
ViewData["files"] = files;
|
ViewData["files"] = files;
|
||||||
|
|
||||||
var clearkey = ApiKeyProtector.TryUnprotectKey(protector, apiKey);
|
var clearkey = protector.Unprotect(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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
|
||||||
|
|
||||||
namespace isnd.Helpers
|
|
||||||
{
|
|
||||||
public static class ApiKeyProtector
|
|
||||||
{
|
|
||||||
private const byte LegacyDelta = 145;
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
unprotected = protector.Unprotect(protectedValue);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (CryptographicException)
|
|
||||||
{
|
|
||||||
unprotected = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (FormatException)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string UnprotectLegacy(string protectedValue)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(protectedValue))
|
|
||||||
{
|
|
||||||
return protectedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bytes = Convert.FromBase64String(protectedValue);
|
|
||||||
var unprotectedBytes = new byte[bytes.Length];
|
|
||||||
for (var index = 0; index < bytes.Length; index++)
|
|
||||||
{
|
|
||||||
unprotectedBytes[index] = (byte)(bytes[index] ^ LegacyDelta);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(unprotectedBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -18,10 +18,6 @@ namespace Isn.tests
|
||||||
{
|
{
|
||||||
private readonly HttpListener listener;
|
private readonly HttpListener listener;
|
||||||
private readonly Task listenerTask;
|
private readonly Task listenerTask;
|
||||||
private readonly string configDirectory;
|
|
||||||
private readonly string originalConfigDirectory;
|
|
||||||
|
|
||||||
public string LastApiKeyHeader { get; private set; }
|
|
||||||
|
|
||||||
public LocalSourceFixture()
|
public LocalSourceFixture()
|
||||||
{
|
{
|
||||||
|
|
@ -49,11 +45,6 @@ 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",
|
||||||
|
|
@ -73,11 +64,6 @@ namespace Isn.tests
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
configDirectory = Path.Combine(Path.GetTempPath(), $"isn-tests-{Guid.NewGuid():N}");
|
|
||||||
Directory.CreateDirectory(configDirectory);
|
|
||||||
originalConfigDirectory = Environment.GetEnvironmentVariable("ISN_CONFIG_DIR");
|
|
||||||
Environment.SetEnvironmentVariable("ISN_CONFIG_DIR", configDirectory);
|
|
||||||
|
|
||||||
SourceUrl = prefix + "index.json";
|
SourceUrl = prefix + "index.json";
|
||||||
ConfigureIsnSettings(SourceUrl);
|
ConfigureIsnSettings(SourceUrl);
|
||||||
Program.LoadConfig();
|
Program.LoadConfig();
|
||||||
|
|
@ -98,12 +84,6 @@ namespace Isn.tests
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable("ISN_CONFIG_DIR", originalConfigDirectory);
|
|
||||||
if (Directory.Exists(configDirectory))
|
|
||||||
{
|
|
||||||
Directory.Delete(configDirectory, recursive: true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetFreePort()
|
private static int GetFreePort()
|
||||||
|
|
@ -115,8 +95,12 @@ namespace Isn.tests
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureIsnSettings(string sourceUrl)
|
private static void ConfigureIsnSettings(string sourceUrl)
|
||||||
{
|
{
|
||||||
|
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||||
|
var configDirectory = Path.Combine(home, ".isn");
|
||||||
|
Directory.CreateDirectory(configDirectory);
|
||||||
|
|
||||||
var configPath = Path.Combine(configDirectory, "config.json");
|
var configPath = Path.Combine(configDirectory, "config.json");
|
||||||
var settings = new Settings
|
var settings = new Settings
|
||||||
{
|
{
|
||||||
|
|
@ -141,44 +125,6 @@ namespace Isn.tests
|
||||||
this.fixture = fixture;
|
this.fixture = fixture;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SupportsIsolatedConfigDirectoryOverride()
|
|
||||||
{
|
|
||||||
var tempDir = Path.Combine(Path.GetTempPath(), $"isn-test-{Guid.NewGuid():N}");
|
|
||||||
Directory.CreateDirectory(tempDir);
|
|
||||||
var originalConfigDir = Environment.GetEnvironmentVariable("ISN_CONFIG_DIR");
|
|
||||||
var configFile = Path.Combine(tempDir, "config.json");
|
|
||||||
var expectedSource = "https://example.test/index.json";
|
|
||||||
var settings = new Settings
|
|
||||||
{
|
|
||||||
DataProtectionTitle = "isn",
|
|
||||||
Sources = new Dictionary<string, SourceSettings>
|
|
||||||
{
|
|
||||||
[expectedSource] = new SourceSettings { Alias = "override" }
|
|
||||||
},
|
|
||||||
DefaultSourceKey = expectedSource
|
|
||||||
};
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("ISN_CONFIG_DIR", tempDir);
|
|
||||||
File.WriteAllText(configFile, JsonConvert.SerializeObject(settings, Formatting.Indented));
|
|
||||||
|
|
||||||
Program.LoadConfig();
|
|
||||||
|
|
||||||
Assert.Equal(expectedSource, Program.Settings.DefaultSourceKey);
|
|
||||||
Assert.True(Program.Settings.Sources.ContainsKey(expectedSource));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("ISN_CONFIG_DIR", originalConfigDir);
|
|
||||||
if (Directory.Exists(tempDir))
|
|
||||||
{
|
|
||||||
Directory.Delete(tempDir, recursive: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HaveADefaultDataProtector()
|
public void HaveADefaultDataProtector()
|
||||||
{
|
{
|
||||||
|
|
@ -213,22 +159,6 @@ 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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,6 @@ using System.Threading.Tasks;
|
||||||
using NuGet.Protocol.Core.Types;
|
using NuGet.Protocol.Core.Types;
|
||||||
using NuGet.Common;
|
using NuGet.Common;
|
||||||
using Isn.Abstract;
|
using Isn.Abstract;
|
||||||
using isnd.Helpers;
|
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
namespace isnd.host.tests
|
namespace isnd.host.tests
|
||||||
{
|
{
|
||||||
|
|
@ -52,27 +49,6 @@ namespace isnd.host.tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void LegacyApiKeyProtectorCanUnprotectValuesFromOlderClients()
|
|
||||||
{
|
|
||||||
const string clearValue = "legacy-api-key";
|
|
||||||
var legacyProtectedValue = new Isn.DefaultDataProtector().Protect(clearValue);
|
|
||||||
|
|
||||||
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]
|
[Fact]
|
||||||
void TestDropUser()
|
void TestDropUser()
|
||||||
{
|
{
|
||||||
|
|
@ -141,14 +117,5 @@ namespace isnd.host.tests
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ThrowingProtector : IDataProtector
|
|
||||||
{
|
|
||||||
public byte[] Protect(byte[] plaintext) => throw new CryptographicException("unexpected protect call");
|
|
||||||
|
|
||||||
public byte[] Unprotect(byte[] protectedData) => throw new CryptographicException("unexpected unprotect call");
|
|
||||||
|
|
||||||
public IDataProtector CreateProtector(string purpose) => this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue