diff --git a/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs b/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs index 8958501b..1bc49c90 100644 --- a/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs +++ b/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using NuGet.Server.Core.Infrastructure; @@ -7,9 +8,9 @@ namespace NuGet.Server.V2.Samples.OwinHost { public class DictionarySettingsProvider : ISettingsProvider { - readonly Dictionary _settings; + readonly Dictionary _settings; - public DictionarySettingsProvider(Dictionary settings) + public DictionarySettingsProvider(Dictionary settings) { _settings = settings; } @@ -18,8 +19,13 @@ public DictionarySettingsProvider(Dictionary settings) public bool GetBoolSetting(string key, bool defaultValue) { System.Diagnostics.Debug.WriteLine("getSetting: " + key); - return _settings.ContainsKey(key) ? _settings[key] : defaultValue; + return _settings.ContainsKey(key) ? Convert.ToBoolean(_settings[key]) : defaultValue; } + + public string GetStringSetting(string key, string defaultValue) + { + return _settings.ContainsKey(key) ? Convert.ToString(_settings[key]) : defaultValue; + } } } diff --git a/samples/NuGet.Server.V2.Samples.OwinHost/Program.cs b/samples/NuGet.Server.V2.Samples.OwinHost/Program.cs index 6f952bd3..46e775f6 100644 --- a/samples/NuGet.Server.V2.Samples.OwinHost/Program.cs +++ b/samples/NuGet.Server.V2.Samples.OwinHost/Program.cs @@ -24,7 +24,7 @@ static void Main(string[] args) // Set up a common settingsProvider to be used by all repositories. // If a setting is not present in dictionary default value will be used. - var settings = new Dictionary(); + var settings = new Dictionary(); settings.Add("enableDelisting", false); //default=false settings.Add("enableFrameworkFiltering", false); //default=false settings.Add("ignoreSymbolsPackages", true); //default=false diff --git a/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs b/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs index 65f31e12..b3c5ce97 100644 --- a/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs +++ b/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs @@ -8,5 +8,10 @@ public bool GetBoolSetting(string key, bool defaultValue) { return defaultValue; } + + public string GetStringSetting(string key, string defaultValue) + { + return defaultValue; + } } } diff --git a/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs b/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs index bd20aa57..6fca200a 100644 --- a/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs +++ b/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs @@ -5,5 +5,6 @@ namespace NuGet.Server.Core.Infrastructure public interface ISettingsProvider { bool GetBoolSetting(string key, bool defaultValue); + string GetStringSetting(string key, string defaultValue); } } diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs index 38aa495c..6dcd9c61 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs @@ -58,7 +58,7 @@ public ServerPackageRepository( _runBackgroundTasks = true; _settingsProvider = settingsProvider ?? new DefaultSettingsProvider(); _logger = logger ?? new TraceLogger(); - _serverPackageCache = InitializeServerPackageStore(); + _serverPackageCache = InitializeServerPackageCache(); _serverPackageStore = new ServerPackageStore( _fileSystem, new ExpandedPackageRepository(_fileSystem, hashProvider), @@ -81,7 +81,7 @@ internal ServerPackageRepository( _runBackgroundTasks = runBackgroundTasks; _settingsProvider = settingsProvider ?? new DefaultSettingsProvider(); _logger = logger ?? new TraceLogger(); - _serverPackageCache = InitializeServerPackageStore(); + _serverPackageCache = InitializeServerPackageCache(); _serverPackageStore = new ServerPackageStore( _fileSystem, innerRepository, @@ -105,9 +105,39 @@ internal ServerPackageRepository( private bool EnableFileSystemMonitoring => _settingsProvider.GetBoolSetting("enableFileSystemMonitoring", true); - private ServerPackageCache InitializeServerPackageStore() + private string CacheFileName => _settingsProvider.GetStringSetting("cacheFileName", null); + + private ServerPackageCache InitializeServerPackageCache() { - return new ServerPackageCache(_fileSystem, Environment.MachineName.ToLowerInvariant() + ".cache.bin"); + return new ServerPackageCache(_fileSystem, ResolveCacheFileName()); + } + + private string ResolveCacheFileName() + { + var fileName = CacheFileName; + const string suffix = ".cache.bin"; + + if (String.IsNullOrWhiteSpace(fileName)) + { + // Default file name + return Environment.MachineName.ToLowerInvariant() + suffix; + } + + if (fileName.LastIndexOfAny(Path.GetInvalidFileNameChars()) > 0) + { + var message = string.Format(Strings.Error_InvalidCacheFileName, fileName); + + _logger.Log(LogLevel.Error, message); + + throw new InvalidOperationException(message); + } + + if (fileName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) + { + return fileName; + } + + return fileName + suffix; } /// diff --git a/src/NuGet.Server.Core/Strings.Designer.cs b/src/NuGet.Server.Core/Strings.Designer.cs index 687e05d0..7aaf25bc 100644 --- a/src/NuGet.Server.Core/Strings.Designer.cs +++ b/src/NuGet.Server.Core/Strings.Designer.cs @@ -60,6 +60,15 @@ internal Strings() { } } + /// + /// Looks up a localized string similar to Configured cache file name '{0}' is invalid. Keep it simple; No paths allowed.. + /// + internal static string Error_InvalidCacheFileName { + get { + return ResourceManager.GetString("Error_InvalidCacheFileName", resourceCulture); + } + } + /// /// Looks up a localized string similar to Package {0} already exists. The server is configured to not allow overwriting packages that already exist.. /// diff --git a/src/NuGet.Server.Core/Strings.resx b/src/NuGet.Server.Core/Strings.resx index f3e2b0b4..9b101590 100644 --- a/src/NuGet.Server.Core/Strings.resx +++ b/src/NuGet.Server.Core/Strings.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Configured cache file name '{0}' is invalid. Keep it simple; No paths allowed. + Package {0} already exists. The server is configured to not allow overwriting packages that already exist. diff --git a/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs b/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs index 658cf083..0e524950 100644 --- a/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs +++ b/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs @@ -28,5 +28,11 @@ public bool GetBoolSetting(string key, bool defaultValue) bool value; return !bool.TryParse(settings[key], out value) ? defaultValue : value; } + + public string GetStringSetting(string key, string defaultValue) + { + var settings = _getSettings(); + return settings[key] ?? defaultValue; + } } } \ No newline at end of file diff --git a/src/NuGet.Server/Web.config b/src/NuGet.Server/Web.config index b084c2ce..6be09215 100644 --- a/src/NuGet.Server/Web.config +++ b/src/NuGet.Server/Web.config @@ -22,6 +22,12 @@ --> + + + diff --git a/test/NuGet.Server.Core.Tests/Infrastructure/FuncSettingsProvider.cs b/test/NuGet.Server.Core.Tests/Infrastructure/FuncSettingsProvider.cs index 87ebf8c2..b18397ed 100644 --- a/test/NuGet.Server.Core.Tests/Infrastructure/FuncSettingsProvider.cs +++ b/test/NuGet.Server.Core.Tests/Infrastructure/FuncSettingsProvider.cs @@ -7,8 +7,8 @@ namespace NuGet.Server.Core.Tests.Infrastructure { class FuncSettingsProvider : ISettingsProvider { - readonly Func _getSetting; - internal FuncSettingsProvider(Func getSetting) + readonly Func _getSetting; + internal FuncSettingsProvider(Func getSetting) { if (getSetting == null) { @@ -20,7 +20,12 @@ internal FuncSettingsProvider(Func getSetting) public bool GetBoolSetting(string key, bool defaultValue) { - return _getSetting(key, defaultValue); + return Convert.ToBoolean(_getSetting(key, defaultValue)); + } + + public string GetStringSetting(string key, string defaultValue) + { + return Convert.ToString(_getSetting(key, defaultValue)); } } } diff --git a/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs b/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs index 6fec5230..b813d942 100644 --- a/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs +++ b/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs @@ -23,7 +23,7 @@ public class ServerPackageRepositoryTest public static async Task CreateServerPackageRepositoryAsync( string path, Action setupRepository = null, - Func getSetting = null) + Func getSetting = null) { var fileSystem = new PhysicalFileSystem(path); var expandedPackageRepository = new ExpandedPackageRepository(fileSystem); @@ -514,7 +514,7 @@ public async Task ServerPackageRepositorySemVer1IsAbsoluteLatest(bool enableDeli using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - var getSetting = enableDelisting ? EnableDelisting : (Func)null; + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "2.0-alpha")); @@ -548,7 +548,7 @@ public async Task ServerPackageRepositorySemVer2IsAbsoluteLatest(bool enableDeli using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - var getSetting = enableDelisting ? EnableDelisting : (Func)null; + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "2.0-alpha")); @@ -603,7 +603,7 @@ public async Task ServerPackageRepositorySemVer1IsLatest(bool enableDelisting) using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - var getSetting = enableDelisting ? EnableDelisting : (Func)null; + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test1", "1.0.0")); @@ -634,7 +634,7 @@ public async Task ServerPackageRepositorySemVer2IsLatest(bool enableDelisting) using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - var getSetting = enableDelisting ? EnableDelisting : (Func)null; + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "1.11")); @@ -811,6 +811,83 @@ public async Task ServerPackageRepositoryAddPackageRejectsDuplicatesWithSemVer2( } } + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public async Task ServerPackageRepository_CustomCacheFileNameNotConfigured_UseMachineNameAsFileName(string fileNameFromConfig) + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + ServerPackageRepository serverRepository = await CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? fileNameFromConfig : defaultValue); + + string expectedCacheFileName = Path.Combine(serverRepository.Source, Environment.MachineName.ToLowerInvariant() + ".cache.bin"); + + Assert.True(File.Exists(expectedCacheFileName)); + } + } + + [Fact] + public async Task ServerPackageRepository_CustomCacheFileNameIsConfigured_CustomCacheFileIsCreated() + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + ServerPackageRepository serverRepository = await CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? "CustomFileName.cache.bin" : defaultValue); + + string expectedCacheFileName = Path.Combine(serverRepository.Source, "CustomFileName.cache.bin"); + + Assert.True(File.Exists(expectedCacheFileName)); + } + } + + [Fact] + public async Task ServerPackageRepository_CustomCacheFileNameWithoutExtensionIsConfigured_CustomCacheFileWithExtensionIsCreated() + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + ServerPackageRepository serverRepository = await CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? "CustomFileName" : defaultValue); + + string expectedCacheFileName = Path.Combine(serverRepository.Source, "CustomFileName.cache.bin"); + + Assert.True(File.Exists(expectedCacheFileName)); + } + } + + [Theory] + [InlineData("c:\\file\\is\\a\\path\\to\\Awesome.cache.bin")] + [InlineData("random:invalidFileName.cache.bin")] + public async Task ServerPackageRepository_CustomCacheFileNameIsInvalid_ThrowUp(string invlaidCacheFileName) + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + Task Code() => CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? invlaidCacheFileName : defaultValue); + + await Assert.ThrowsAsync(Code); + } + } + + [Fact] + public async Task ServerPackageRepository_CustomCacheFileNameIsInvalid_ThrowUpWithCorrectErrorMessage() + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + Task Code() => CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? "foo:bar/baz" : defaultValue); + + var expectedMessage = "Configured cache file name 'foo:bar/baz' is invalid. Keep it simple; No paths allowed."; + Assert.Equal(expectedMessage, (await Assert.ThrowsAsync(Code)).Message); + } + } + private static IPackage CreateMockPackage(string id, string version) { var package = new Mock(); @@ -879,7 +956,7 @@ private IPackage CreatePackage( return outputPackage; } - private static bool EnableDelisting(string key, bool defaultValue) + private static object EnableDelisting(string key, object defaultValue) { if (key == "enableDelisting") {