Move dotnet-user-secrets into current repo
This commit is contained in:
parent
3b351ad1bb
commit
8f2eccbd36
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25123.0
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{66517987-2A5A-4330-B130-207039378FD4}"
|
||||
EndProject
|
||||
|
|
@ -29,6 +29,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AppWithDeps", "test\TestApp
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dependency", "test\TestApps\Dependency\Dependency.xproj", "{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.SecretManager.Tools", "src\Microsoft.Extensions.SecretManager.Tools\Microsoft.Extensions.SecretManager.Tools.xproj", "{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.SecretManager.Tools.Tests", "test\Microsoft.Extensions.SecretManager.Tools.Tests\Microsoft.Extensions.SecretManager.Tools.Tests.xproj", "{7B331122-83B1-4F08-A119-DC846959844C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -63,6 +67,14 @@ Global
|
|||
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B331122-83B1-4F08-A119-DC846959844C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7B331122-83B1-4F08-A119-DC846959844C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7B331122-83B1-4F08-A119-DC846959844C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B331122-83B1-4F08-A119-DC846959844C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -76,5 +88,7 @@ Global
|
|||
{2AB1A28B-2022-49EA-AF77-AC8A875915CC} = {2876B12E-5841-4792-85A8-2929AEE11885}
|
||||
{F7734E61-F510-41E0-AD15-301A64081CD1} = {2876B12E-5841-4792-85A8-2929AEE11885}
|
||||
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA} = {2876B12E-5841-4792-85A8-2929AEE11885}
|
||||
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E} = {66517987-2A5A-4330-B130-207039378FD4}
|
||||
{7B331122-83B1-4F08-A119-DC846959844C} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
@ -5,7 +5,8 @@
|
|||
],
|
||||
"packages": {
|
||||
"Microsoft.DotNet.Watcher.Tools": { },
|
||||
"Microsoft.DotNet.Watcher.Core": { }
|
||||
"Microsoft.DotNet.Watcher.Core": { },
|
||||
"Microsoft.Extensions.SecretManager.Tools": { }
|
||||
}
|
||||
},
|
||||
"Default": { // Rules to run for packages not listed in any other set.
|
||||
|
|
|
|||
50
README.md
50
README.md
|
|
@ -1,48 +1,12 @@
|
|||
dotnet-watch
|
||||
===
|
||||
`dotnet-watch` is a file watcher for `dotnet` that restarts the specified application when changes in the source code are detected.
|
||||
DotNetTools
|
||||
===========
|
||||
|
||||
### How To Install
|
||||
[](https://travis-ci.org/aspnet/dotnet-watch/branches)
|
||||
[](https://ci.appveyor.com/project/aspnetci/dnx-watch/branch/dev)
|
||||
|
||||
Add `Microsoft.DotNet.Watcher.Tools` to the `tools` section of your `project.json` file:
|
||||
The project contains command-line tools for the .NET Core SDK.
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"tools": {
|
||||
"Microsoft.DotNet.Watcher.Tools": {
|
||||
"version": "1.0.0-*",
|
||||
"imports": "portable-net451+win8"
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### How To Use
|
||||
|
||||
dotnet watch [dotnet arguments]
|
||||
|
||||
Add `watch` after `dotnet` in the command that you want to run:
|
||||
|
||||
| What you want to run | Dotnet watch command |
|
||||
| ---------------------------------------------- | -------------------------------------------------------- |
|
||||
| dotnet run | dotnet **watch** run |
|
||||
| dotnet run --arg1 value1 | dotnet **watch** run --arg1 value |
|
||||
| dotnet run --framework net451 -- --arg1 value1 | dotnet **watch** run --framework net451 -- --arg1 value1 |
|
||||
| dotnet test | dotnet **watch** test |
|
||||
|
||||
### Advanced configuration options
|
||||
|
||||
Configuration options can be passed to `dotnet watch` through environment variables. The available variables are:
|
||||
|
||||
| Variable | Effect |
|
||||
| ---------------------------------------------- | -------------------------------------------------------- |
|
||||
| DOTNET_USE_POLLING_FILE_WATCHER | If set to "1" or "true", `dotnet watch` will use a polling file watcher instead of CoreFx's `FileSystemWatcher`. Used when watching files on network shares or Docker mounted volumes. |
|
||||
| DOTNET_WATCH_LOG_LEVEL | Used to set the logging level for messages coming from `dotnet watch`. Accepted values `None`, `Trace`, `Debug`, `Information`, `Warning`, `Error`, `Critical`. Default: `Information`. |
|
||||
|
||||
AppVeyor: [](https://ci.appveyor.com/project/aspnetci/dnx-watch/branch/dev)
|
||||
|
||||
Travis: [](https://travis-ci.org/aspnet/dotnet-watch)
|
||||
- [dotnet-watch](src/Microsoft.DotNet.Watcher.Tools/)
|
||||
- [dotnet-user-secrets](src/Microsoft.Extensions.SecretManager.Tools/)
|
||||
|
||||
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
dotnet-watch
|
||||
============
|
||||
`dotnet-watch` is a file watcher for `dotnet` that restarts the specified application when changes in the source code are detected.
|
||||
|
||||
### How To Install
|
||||
|
||||
Add `Microsoft.DotNet.Watcher.Tools` to the `tools` section of your `project.json` file:
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"tools": {
|
||||
"Microsoft.DotNet.Watcher.Tools": "1.0.0-*"
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### How To Use
|
||||
|
||||
dotnet watch [dotnet arguments]
|
||||
|
||||
Add `watch` after `dotnet` in the command that you want to run:
|
||||
|
||||
| What you want to run | Dotnet watch command |
|
||||
| ---------------------------------------------- | -------------------------------------------------------- |
|
||||
| dotnet run | dotnet **watch** run |
|
||||
| dotnet run --arg1 value1 | dotnet **watch** run --arg1 value |
|
||||
| dotnet run --framework net451 -- --arg1 value1 | dotnet **watch** run --framework net451 -- --arg1 value1 |
|
||||
| dotnet test | dotnet **watch** test |
|
||||
|
||||
### Advanced configuration options
|
||||
|
||||
Configuration options can be passed to `dotnet watch` through environment variables. The available variables are:
|
||||
|
||||
| Variable | Effect |
|
||||
| ---------------------------------------------- | -------------------------------------------------------- |
|
||||
| DOTNET_USE_POLLING_FILE_WATCHER | If set to "1" or "true", `dotnet watch` will use a polling file watcher instead of CoreFx's `FileSystemWatcher`. Used when watching files on network shares or Docker mounted volumes. |
|
||||
| DOTNET_WATCH_LOG_LEVEL | Used to set the logging level for messages coming from `dotnet watch`. Accepted values `None`, `Trace`, `Debug`, `Information`, `Warning`, `Error`, `Critical`. Default: `Information`. |
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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 Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Logger to print formatted command output.
|
||||
/// </summary>
|
||||
public class CommandOutputLogger : ILogger
|
||||
{
|
||||
private readonly CommandOutputProvider _provider;
|
||||
private readonly AnsiConsole _outConsole;
|
||||
|
||||
public CommandOutputLogger(CommandOutputProvider commandOutputProvider, bool useConsoleColor)
|
||||
{
|
||||
_provider = commandOutputProvider;
|
||||
_outConsole = AnsiConsole.GetOutput(useConsoleColor);
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
if (logLevel < _provider.LogLevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (IsEnabled(logLevel))
|
||||
{
|
||||
_outConsole.WriteLine(string.Format("{0}: {1}", Caption(logLevel), formatter(state, exception)));
|
||||
}
|
||||
}
|
||||
|
||||
private string Caption(LogLevel logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Trace: return "\x1b[35mtrace\x1b[39m";
|
||||
case LogLevel.Debug: return "\x1b[35mdebug\x1b[39m";
|
||||
case LogLevel.Information: return "\x1b[32minfo\x1b[39m";
|
||||
case LogLevel.Warning: return "\x1b[33mwarn\x1b[39m";
|
||||
case LogLevel.Error: return "\x1b[31mfail\x1b[39m";
|
||||
case LogLevel.Critical: return "\x1b[31mcritical\x1b[39m";
|
||||
}
|
||||
|
||||
throw new Exception("Unknown LogLevel");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools
|
||||
{
|
||||
public class CommandOutputProvider : ILoggerProvider
|
||||
{
|
||||
public ILogger CreateLogger(string name)
|
||||
{
|
||||
var useConsoleColor = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
return new CommandOutputLogger(this, useConsoleColor);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public LogLevel LogLevel { get; set; } = LogLevel.Information;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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 Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class ClearCommand : ICommand
|
||||
{
|
||||
public static void Configure(CommandLineApplication command, CommandLineOptions options)
|
||||
{
|
||||
command.Description = "Deletes all the application secrets";
|
||||
command.HelpOption();
|
||||
|
||||
command.OnExecute(() =>
|
||||
{
|
||||
options.Command = new ClearCommand();
|
||||
});
|
||||
}
|
||||
|
||||
public void Execute(SecretsStore store, ILogger logger)
|
||||
{
|
||||
store.Clear();
|
||||
store.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.Extensions.CommandLineUtils
|
||||
{
|
||||
public static class UserSecretsCommandLineExtensions
|
||||
{
|
||||
public static CommandOption HelpOption(this CommandLineApplication app)
|
||||
{
|
||||
return app.HelpOption("-?|-h|--help");
|
||||
}
|
||||
|
||||
public static void OnExecute(this CommandLineApplication app, Action action)
|
||||
{
|
||||
app.OnExecute(() =>
|
||||
{
|
||||
action();
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// 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.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
public class CommandLineOptions
|
||||
{
|
||||
public bool IsVerbose { get; set; }
|
||||
public bool IsHelp { get; set; }
|
||||
public string Project { get; set; }
|
||||
internal ICommand Command { get; set; }
|
||||
|
||||
public static CommandLineOptions Parse(string[] args, TextWriter output)
|
||||
{
|
||||
var app = new CommandLineApplication()
|
||||
{
|
||||
Out = output,
|
||||
Name = "dotnet user-secrets",
|
||||
FullName = "User Secrets Manager",
|
||||
Description = "Manages user secrets"
|
||||
};
|
||||
|
||||
app.HelpOption();
|
||||
app.VersionOption("--version", GetInformationalVersion());
|
||||
|
||||
var optionVerbose = app.Option("-v|--verbose", "Verbose output",
|
||||
CommandOptionType.NoValue, inherited: true);
|
||||
|
||||
var optionProject = app.Option("-p|--project <PROJECT>", "Path to project, default is current directory",
|
||||
CommandOptionType.SingleValue, inherited: true);
|
||||
|
||||
var options = new CommandLineOptions();
|
||||
app.Command("set", c => SetCommand.Configure(c, options));
|
||||
app.Command("remove", c => RemoveCommand.Configure(c, options));
|
||||
app.Command("list", c => ListCommand.Configure(c, options));
|
||||
app.Command("clear", c => ClearCommand.Configure(c, options));
|
||||
|
||||
// Show help information if no subcommand/option was specified.
|
||||
app.OnExecute(() => app.ShowHelp());
|
||||
|
||||
if (app.Execute(args) != 0)
|
||||
{
|
||||
// when command line parsing error in subcommand
|
||||
return null;
|
||||
}
|
||||
|
||||
options.IsHelp = app.IsShowingInformation;
|
||||
options.IsVerbose = optionVerbose.HasValue();
|
||||
options.Project = optionProject.Value();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private static string GetInformationalVersion()
|
||||
{
|
||||
var assembly = typeof(Program).GetTypeInfo().Assembly;
|
||||
var attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
|
||||
var versionAttribute = attribute == null ?
|
||||
assembly.GetName().Version.ToString() :
|
||||
attribute.InformationalVersion;
|
||||
|
||||
return versionAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception whose stack trace should be suppressed in console output
|
||||
/// </summary>
|
||||
public class GracefulException : Exception
|
||||
{
|
||||
public GracefulException()
|
||||
{
|
||||
}
|
||||
|
||||
public GracefulException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public GracefulException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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 Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal interface ICommand
|
||||
{
|
||||
void Execute(SecretsStore store, ILogger logger);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// 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 Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class ListCommand : ICommand
|
||||
{
|
||||
public static void Configure(CommandLineApplication command, CommandLineOptions options)
|
||||
{
|
||||
command.Description = "Lists all the application secrets";
|
||||
command.HelpOption();
|
||||
|
||||
command.OnExecute(() =>
|
||||
{
|
||||
options.Command = new ListCommand();
|
||||
});
|
||||
}
|
||||
|
||||
public void Execute(SecretsStore store, ILogger logger)
|
||||
{
|
||||
if (store.Count == 0)
|
||||
{
|
||||
logger.LogInformation(Resources.Error_No_Secrets_Found);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var secret in store.AsEnumerable())
|
||||
{
|
||||
logger.LogInformation(Resources.FormatMessage_Secret_Value_Format(secret.Key, secret.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// 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 Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class RemoveCommand : ICommand
|
||||
{
|
||||
private readonly string _keyName;
|
||||
|
||||
public static void Configure(CommandLineApplication command, CommandLineOptions options)
|
||||
{
|
||||
command.Description = "Removes the specified user secret";
|
||||
command.HelpOption();
|
||||
|
||||
var keyArg = command.Argument("[name]", "Name of the secret");
|
||||
command.OnExecute(() =>
|
||||
{
|
||||
if (keyArg.Value == null)
|
||||
{
|
||||
throw new GracefulException("Missing parameter value for 'name'.\nUse the '--help' flag to see info.");
|
||||
}
|
||||
|
||||
options.Command = new RemoveCommand(keyArg.Value);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public RemoveCommand(string keyName)
|
||||
{
|
||||
_keyName = keyName;
|
||||
}
|
||||
|
||||
public void Execute(SecretsStore store, ILogger logger)
|
||||
{
|
||||
if (!store.ContainsKey(_keyName))
|
||||
{
|
||||
logger.LogWarning(Resources.Error_Missing_Secret, _keyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
store.Remove(_keyName);
|
||||
store.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// 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 System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration.UserSecrets;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class SecretsStore
|
||||
{
|
||||
private readonly string _secretsFilePath;
|
||||
private IDictionary<string, string> _secrets;
|
||||
|
||||
public SecretsStore(string userSecretsId, ILogger logger)
|
||||
{
|
||||
if (userSecretsId == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(userSecretsId));
|
||||
}
|
||||
|
||||
_secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId);
|
||||
logger.LogDebug(Resources.Message_Secret_File_Path, _secretsFilePath);
|
||||
|
||||
// workaround https://github.com/aspnet/Configuration/issues/478
|
||||
// TODO remove when tool upgrades to use 1.1.0
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(_secretsFilePath));
|
||||
//end workaround
|
||||
|
||||
_secrets = new ConfigurationBuilder()
|
||||
.AddJsonFile(_secretsFilePath, optional: true)
|
||||
.Build()
|
||||
.AsEnumerable()
|
||||
.Where(i => i.Value != null)
|
||||
.ToDictionary(i => i.Key, i => i.Value, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public int Count => _secrets.Count;
|
||||
|
||||
public bool ContainsKey(string key) => _secrets.ContainsKey(key);
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> AsEnumerable() => _secrets;
|
||||
|
||||
public void Clear() => _secrets.Clear();
|
||||
|
||||
public void Set(string key, string value) => _secrets[key] = value;
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
if (_secrets.ContainsKey(key))
|
||||
{
|
||||
_secrets.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(_secretsFilePath));
|
||||
|
||||
var contents = new JObject();
|
||||
if (_secrets != null)
|
||||
{
|
||||
foreach (var secret in _secrets.AsEnumerable())
|
||||
{
|
||||
contents[secret.Key] = secret.Value;
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(_secretsFilePath, contents.ToString(), Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// 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 Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class SetCommand : ICommand
|
||||
{
|
||||
private readonly string _keyName;
|
||||
private readonly string _keyValue;
|
||||
|
||||
public static void Configure(CommandLineApplication command, CommandLineOptions options)
|
||||
{
|
||||
command.Description = "Sets the user secret to the specified value";
|
||||
command.HelpOption();
|
||||
|
||||
var keyArg = command.Argument("[name]", "Name of the secret");
|
||||
var valueArg = command.Argument("[value]", "Value of the secret");
|
||||
|
||||
command.OnExecute(() =>
|
||||
{
|
||||
if (keyArg.Value == null)
|
||||
{
|
||||
throw new GracefulException("Missing parameter value for 'name'.\nUse the '--help' flag to see info.");
|
||||
}
|
||||
|
||||
if (valueArg.Value == null)
|
||||
{
|
||||
throw new GracefulException("Missing parameter value for 'value'.\nUse the '--help' flag to see info.");
|
||||
}
|
||||
|
||||
options.Command = new SetCommand(keyArg.Value, valueArg.Value);
|
||||
});
|
||||
}
|
||||
|
||||
public SetCommand(string keyName, string keyValue)
|
||||
{
|
||||
_keyName = keyName;
|
||||
_keyValue = keyValue;
|
||||
}
|
||||
|
||||
public void Execute(SecretsStore store, ILogger logger)
|
||||
{
|
||||
store.Set(_keyName, _keyValue);
|
||||
store.Save();
|
||||
logger.LogInformation(Resources.Message_Saved_Secret, _keyName, _keyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>8730e848-ca0f-4e0a-9a2f-bc22ad0b2c4e</ProjectGuid>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders.Physical;
|
||||
using Microsoft.Extensions.SecretManager.Tools.Internal;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private ILogger _logger;
|
||||
private CommandOutputProvider _loggerProvider;
|
||||
private readonly TextWriter _consoleOutput;
|
||||
private readonly string _workingDirectory;
|
||||
|
||||
public Program()
|
||||
: this(Console.Out, Directory.GetCurrentDirectory())
|
||||
{
|
||||
}
|
||||
|
||||
internal Program(TextWriter consoleOutput, string workingDirectory)
|
||||
{
|
||||
_consoleOutput = consoleOutput;
|
||||
_workingDirectory = workingDirectory;
|
||||
|
||||
var loggerFactory = new LoggerFactory();
|
||||
CommandOutputProvider = new CommandOutputProvider();
|
||||
loggerFactory.AddProvider(CommandOutputProvider);
|
||||
Logger = loggerFactory.CreateLogger<Program>();
|
||||
}
|
||||
|
||||
public ILogger Logger
|
||||
{
|
||||
get { return _logger; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
_logger = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CommandOutputProvider CommandOutputProvider
|
||||
{
|
||||
get { return _loggerProvider; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
_loggerProvider = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
HandleDebugFlag(ref args);
|
||||
|
||||
int rc;
|
||||
new Program().TryRun(args, out rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private static void HandleDebugFlag(ref string[] args)
|
||||
{
|
||||
for (var i = 0; i < args.Length; ++i)
|
||||
{
|
||||
if (args[i] == "--debug")
|
||||
{
|
||||
Console.WriteLine("Process ID " + Process.GetCurrentProcess().Id);
|
||||
Console.WriteLine("Paused for debugger. Press ENTER to continue");
|
||||
Console.ReadLine();
|
||||
|
||||
args = args.Take(i).Concat(args.Skip(i + 1)).ToArray();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryRun(string[] args, out int returnCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
returnCode = RunInternal(args);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is GracefulException)
|
||||
{
|
||||
Logger.LogError(exception.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogDebug(exception.ToString());
|
||||
Logger.LogCritical(Resources.Error_Command_Failed, exception.Message);
|
||||
}
|
||||
returnCode = 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal int RunInternal(params string[] args)
|
||||
{
|
||||
var options = CommandLineOptions.Parse(args, _consoleOutput);
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (options.IsHelp)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (options.IsVerbose)
|
||||
{
|
||||
CommandOutputProvider.LogLevel = LogLevel.Debug;
|
||||
}
|
||||
|
||||
var userSecretsId = ResolveUserSecretsId(options);
|
||||
var store = new SecretsStore(userSecretsId, Logger);
|
||||
options.Command.Execute(store, Logger);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private string ResolveUserSecretsId(CommandLineOptions options)
|
||||
{
|
||||
var projectPath = options.Project ?? _workingDirectory;
|
||||
|
||||
if (!projectPath.EndsWith("project.json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
projectPath = Path.Combine(projectPath, "project.json");
|
||||
}
|
||||
|
||||
var fileInfo = new PhysicalFileInfo(new FileInfo(projectPath));
|
||||
|
||||
Logger.LogDebug(Resources.Message_Project_File_Path, fileInfo.PhysicalPath);
|
||||
return ReadUserSecretsId(fileInfo);
|
||||
}
|
||||
|
||||
// TODO can use runtime API when upgrading to 1.1
|
||||
private string ReadUserSecretsId(IFileInfo fileInfo)
|
||||
{
|
||||
if (fileInfo == null || !fileInfo.Exists)
|
||||
{
|
||||
throw new GracefulException($"Could not find file '{fileInfo.PhysicalPath}'");
|
||||
}
|
||||
|
||||
using (var stream = fileInfo.CreateReadStream())
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
using (var jsonReader = new JsonTextReader(streamReader))
|
||||
{
|
||||
var obj = JObject.Load(jsonReader);
|
||||
|
||||
var userSecretsId = obj.Value<string>("userSecretsId");
|
||||
|
||||
if (string.IsNullOrEmpty(userSecretsId))
|
||||
{
|
||||
throw new GracefulException($"Could not find 'userSecretsId' in json file '{fileInfo.PhysicalPath}'");
|
||||
}
|
||||
|
||||
return userSecretsId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// 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.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyMetadata("Serviceable", "True")]
|
||||
[assembly: NeutralResourcesLanguage("en-us")]
|
||||
[assembly: AssemblyCompany("Microsoft Corporation.")]
|
||||
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
|
||||
[assembly: AssemblyProduct("Microsoft .NET Extensions")]
|
||||
|
|
@ -0,0 +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.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.Extensions.SecretManager.Tools.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
142
src/Microsoft.Extensions.SecretManager.Tools/Properties/Resources.Designer.cs
generated
Normal file
142
src/Microsoft.Extensions.SecretManager.Tools/Properties/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.Extensions.SecretManager.Tools
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.Extensions.SecretManager.Tools.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Command failed : {message}
|
||||
/// </summary>
|
||||
internal static string Error_Command_Failed
|
||||
{
|
||||
get { return GetString("Error_Command_Failed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command failed : {message}
|
||||
/// </summary>
|
||||
internal static string FormatError_Command_Failed(object message)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_Command_Failed", "message"), message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot find '{key}' in the secret store.
|
||||
/// </summary>
|
||||
internal static string Error_Missing_Secret
|
||||
{
|
||||
get { return GetString("Error_Missing_Secret"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot find '{key}' in the secret store.
|
||||
/// </summary>
|
||||
internal static string FormatError_Missing_Secret(object key)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_Missing_Secret", "key"), key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No secrets configured for this application.
|
||||
/// </summary>
|
||||
internal static string Error_No_Secrets_Found
|
||||
{
|
||||
get { return GetString("Error_No_Secrets_Found"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No secrets configured for this application.
|
||||
/// </summary>
|
||||
internal static string FormatError_No_Secrets_Found()
|
||||
{
|
||||
return GetString("Error_No_Secrets_Found");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Project file path {project}.
|
||||
/// </summary>
|
||||
internal static string Message_Project_File_Path
|
||||
{
|
||||
get { return GetString("Message_Project_File_Path"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Project file path {project}.
|
||||
/// </summary>
|
||||
internal static string FormatMessage_Project_File_Path(object project)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Message_Project_File_Path", "project"), project);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Successfully saved {key} = {value} to the secret store.
|
||||
/// </summary>
|
||||
internal static string Message_Saved_Secret
|
||||
{
|
||||
get { return GetString("Message_Saved_Secret"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Successfully saved {key} = {value} to the secret store.
|
||||
/// </summary>
|
||||
internal static string FormatMessage_Saved_Secret(object key, object value)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Message_Saved_Secret", "key", "value"), key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Secrets file path {secretsFilePath}.
|
||||
/// </summary>
|
||||
internal static string Message_Secret_File_Path
|
||||
{
|
||||
get { return GetString("Message_Secret_File_Path"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Secrets file path {secretsFilePath}.
|
||||
/// </summary>
|
||||
internal static string FormatMessage_Secret_File_Path(object secretsFilePath)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Message_Secret_File_Path", "secretsFilePath"), secretsFilePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// {key} = {value}
|
||||
/// </summary>
|
||||
internal static string Message_Secret_Value_Format
|
||||
{
|
||||
get { return GetString("Message_Secret_Value_Format"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// {key} = {value}
|
||||
/// </summary>
|
||||
internal static string FormatMessage_Secret_Value_Format(object key, object value)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Message_Secret_Value_Format", "key", "value"), key, value);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Error_Command_Failed" xml:space="preserve">
|
||||
<value>Command failed : {message}</value>
|
||||
</data>
|
||||
<data name="Error_Missing_Secret" xml:space="preserve">
|
||||
<value>Cannot find '{key}' in the secret store.</value>
|
||||
</data>
|
||||
<data name="Error_No_Secrets_Found" xml:space="preserve">
|
||||
<value>No secrets configured for this application.</value>
|
||||
</data>
|
||||
<data name="Message_Project_File_Path" xml:space="preserve">
|
||||
<value>Project file path {project}.</value>
|
||||
</data>
|
||||
<data name="Message_Saved_Secret" xml:space="preserve">
|
||||
<value>Successfully saved {key} = {value} to the secret store.</value>
|
||||
</data>
|
||||
<data name="Message_Secret_File_Path" xml:space="preserve">
|
||||
<value>Secrets file path {secretsFilePath}.</value>
|
||||
</data>
|
||||
<data name="Message_Secret_Value_Format" xml:space="preserve">
|
||||
<value>{key} = {value}</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"outputName": "dotnet-user-secrets",
|
||||
"emitEntryPoint": true,
|
||||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk",
|
||||
"nowarn": [
|
||||
"CS1591"
|
||||
],
|
||||
"xmlDoc": true
|
||||
},
|
||||
"description": "Command line tool to manage user secrets for Microsoft.Extensions.Configuration.",
|
||||
"packOptions": {
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aspnet/DotNetTools"
|
||||
},
|
||||
"tags": [
|
||||
"configuration",
|
||||
"secrets",
|
||||
"usersecrets"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.UserSecrets": "1.1.0-*",
|
||||
"Microsoft.Extensions.CommandLineUtils": "1.1.0-*",
|
||||
"Microsoft.Extensions.Logging": "1.1.0-*",
|
||||
"Newtonsoft.Json": "9.0.1",
|
||||
"System.Runtime.InteropServices.RuntimeInformation": "4.0.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.1.1"
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"version": "1.0.0",
|
||||
"type": "platform"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>7b331122-83b1-4f08-a119-dc846959844c</ProjectGuid>
|
||||
<RootNamespace>Microsoft.Extensions.SecretManager.Tools.Tests</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
// 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 System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Configuration.UserSecrets;
|
||||
using Microsoft.Extensions.Configuration.UserSecrets.Tests;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
||||
{
|
||||
public class SecretManagerTests
|
||||
{
|
||||
private TestLogger _logger;
|
||||
|
||||
public SecretManagerTests(ITestOutputHelper output)
|
||||
{
|
||||
_logger = new TestLogger(output);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void SetSecrets(bool fromCurrentDirectory)
|
||||
{
|
||||
var secrets = new KeyValuePair<string, string>[]
|
||||
{
|
||||
new KeyValuePair<string, string>("key1", Guid.NewGuid().ToString()),
|
||||
new KeyValuePair<string, string>("Facebook:AppId", Guid.NewGuid().ToString()),
|
||||
new KeyValuePair<string, string>(@"key-@\/.~123!#$%^&*())-+==", @"key-@\/.~123!#$%^&*())-+=="),
|
||||
new KeyValuePair<string, string>("key2", string.Empty)
|
||||
};
|
||||
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
|
||||
var dir = fromCurrentDirectory
|
||||
? projectPath
|
||||
: Path.GetTempPath();
|
||||
var secretManager = new Program(Console.Out, dir) { Logger = _logger };
|
||||
|
||||
foreach (var secret in secrets)
|
||||
{
|
||||
var parameters = fromCurrentDirectory ?
|
||||
new string[] { "set", secret.Key, secret.Value } :
|
||||
new string[] { "set", secret.Key, secret.Value, "-p", projectPath };
|
||||
secretManager.RunInternal(parameters);
|
||||
}
|
||||
|
||||
Assert.Equal(4, _logger.Messages.Count);
|
||||
|
||||
foreach (var keyValue in secrets)
|
||||
{
|
||||
Assert.Contains(
|
||||
string.Format("Successfully saved {0} = {1} to the secret store.", keyValue.Key, keyValue.Value),
|
||||
_logger.Messages);
|
||||
}
|
||||
|
||||
_logger.Messages.Clear();
|
||||
var args = fromCurrentDirectory
|
||||
? new string[] { "list" }
|
||||
: new string[] { "list", "-p", projectPath };
|
||||
secretManager.RunInternal(args);
|
||||
Assert.Equal(4, _logger.Messages.Count);
|
||||
foreach (var keyValue in secrets)
|
||||
{
|
||||
Assert.Contains(
|
||||
string.Format("{0} = {1}", keyValue.Key, keyValue.Value),
|
||||
_logger.Messages);
|
||||
}
|
||||
|
||||
// Remove secrets.
|
||||
_logger.Messages.Clear();
|
||||
foreach (var secret in secrets)
|
||||
{
|
||||
var parameters = fromCurrentDirectory ?
|
||||
new string[] { "remove", secret.Key } :
|
||||
new string[] { "remove", secret.Key, "-p", projectPath };
|
||||
secretManager.RunInternal(parameters);
|
||||
}
|
||||
|
||||
// Verify secrets are removed.
|
||||
_logger.Messages.Clear();
|
||||
args = fromCurrentDirectory
|
||||
? new string[] { "list" }
|
||||
: new string[] { "list", "-p", projectPath };
|
||||
secretManager.RunInternal(args);
|
||||
Assert.Equal(1, _logger.Messages.Count);
|
||||
Assert.Contains(Resources.Error_No_Secrets_Found, _logger.Messages);
|
||||
|
||||
UserSecretHelper.DeleteTempSecretProject(projectPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetSecret_Update_Existing_Secret()
|
||||
{
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
var secretManager = new Program() { Logger = _logger };
|
||||
|
||||
secretManager.RunInternal("set", "secret1", "value1", "-p", projectPath);
|
||||
Assert.Equal(1, _logger.Messages.Count);
|
||||
Assert.Contains("Successfully saved secret1 = value1 to the secret store.", _logger.Messages);
|
||||
secretManager.RunInternal("set", "secret1", "value2", "-p", projectPath);
|
||||
Assert.Equal(2, _logger.Messages.Count);
|
||||
Assert.Contains("Successfully saved secret1 = value2 to the secret store.", _logger.Messages);
|
||||
|
||||
_logger.Messages.Clear();
|
||||
|
||||
secretManager.RunInternal("list", "-p", projectPath);
|
||||
Assert.Equal(1, _logger.Messages.Count);
|
||||
Assert.Contains("secret1 = value2", _logger.Messages);
|
||||
|
||||
UserSecretHelper.DeleteTempSecretProject(projectPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetSecret_With_Verbose_Flag()
|
||||
{
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
_logger.SetLevel(LogLevel.Debug);
|
||||
var secretManager = new Program() { Logger = _logger };
|
||||
|
||||
secretManager.RunInternal("-v", "set", "secret1", "value1", "-p", projectPath);
|
||||
Assert.Equal(3, _logger.Messages.Count);
|
||||
Assert.Contains(string.Format("Project file path {0}.", Path.Combine(projectPath, "project.json")), _logger.Messages);
|
||||
Assert.Contains(string.Format("Secrets file path {0}.", PathHelper.GetSecretsPath(projectPath)), _logger.Messages);
|
||||
Assert.Contains("Successfully saved secret1 = value1 to the secret store.", _logger.Messages);
|
||||
_logger.Messages.Clear();
|
||||
|
||||
secretManager.RunInternal("-v", "list", "-p", projectPath);
|
||||
|
||||
Assert.Equal(3, _logger.Messages.Count);
|
||||
Assert.Contains(string.Format("Project file path {0}.", Path.Combine(projectPath, "project.json")), _logger.Messages);
|
||||
Assert.Contains(string.Format("Secrets file path {0}.", PathHelper.GetSecretsPath(projectPath)), _logger.Messages);
|
||||
Assert.Contains("secret1 = value1", _logger.Messages);
|
||||
|
||||
UserSecretHelper.DeleteTempSecretProject(projectPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_Non_Existing_Secret()
|
||||
{
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
var secretManager = new Program() { Logger = _logger };
|
||||
secretManager.RunInternal("remove", "secret1", "-p", projectPath);
|
||||
Assert.Equal(1, _logger.Messages.Count);
|
||||
Assert.Contains("Cannot find 'secret1' in the secret store.", _logger.Messages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_Is_Case_Insensitive()
|
||||
{
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
var secretManager = new Program() { Logger = _logger };
|
||||
secretManager.RunInternal("set", "SeCreT1", "value", "-p", projectPath);
|
||||
secretManager.RunInternal("list", "-p", projectPath);
|
||||
Assert.Contains("SeCreT1 = value", _logger.Messages);
|
||||
secretManager.RunInternal("remove", "secret1", "-p", projectPath);
|
||||
|
||||
Assert.Equal(2, _logger.Messages.Count);
|
||||
_logger.Messages.Clear();
|
||||
secretManager.RunInternal("list", "-p", projectPath);
|
||||
|
||||
Assert.Contains(Resources.Error_No_Secrets_Found, _logger.Messages);
|
||||
|
||||
UserSecretHelper.DeleteTempSecretProject(projectPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void List_Flattens_Nested_Objects()
|
||||
{
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
var secretsFile = PathHelper.GetSecretsPath(projectPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(secretsFile));
|
||||
File.WriteAllText(secretsFile, @"{ ""AzureAd"": { ""ClientSecret"": ""abcd郩˙î""} }", Encoding.UTF8);
|
||||
var secretManager = new Program() { Logger = _logger };
|
||||
secretManager.RunInternal("list", "-p", projectPath);
|
||||
Assert.Equal(1, _logger.Messages.Count);
|
||||
Assert.Contains("AzureAd:ClientSecret = abcd郩˙î", _logger.Messages);
|
||||
|
||||
UserSecretHelper.DeleteTempSecretProject(projectPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_Flattens_Nested_Objects()
|
||||
{
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
var secretsFile = PathHelper.GetSecretsPath(projectPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(secretsFile));
|
||||
File.WriteAllText(secretsFile, @"{ ""AzureAd"": { ""ClientSecret"": ""abcd郩˙î""} }", Encoding.UTF8);
|
||||
var secretManager = new Program() { Logger = _logger };
|
||||
secretManager.RunInternal("set", "AzureAd:ClientSecret", "¡™£¢∞", "-p", projectPath);
|
||||
Assert.Equal(1, _logger.Messages.Count);
|
||||
secretManager.RunInternal("list", "-p", projectPath);
|
||||
|
||||
Assert.Equal(2, _logger.Messages.Count);
|
||||
Assert.Contains("AzureAd:ClientSecret = ¡™£¢∞", _logger.Messages);
|
||||
var fileContents = File.ReadAllText(secretsFile, Encoding.UTF8);
|
||||
Assert.Equal(@"{
|
||||
""AzureAd:ClientSecret"": ""¡™£¢∞""
|
||||
}",
|
||||
fileContents, ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true);
|
||||
|
||||
UserSecretHelper.DeleteTempSecretProject(projectPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void List_Empty_Secrets_File()
|
||||
{
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
var secretManager = new Program() { Logger = _logger };
|
||||
secretManager.RunInternal("list", "-p", projectPath);
|
||||
Assert.Equal(1, _logger.Messages.Count);
|
||||
Assert.Contains(Resources.Error_No_Secrets_Found, _logger.Messages);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void Clear_Secrets(bool fromCurrentDirectory)
|
||||
{
|
||||
var projectPath = UserSecretHelper.GetTempSecretProject();
|
||||
|
||||
var dir = fromCurrentDirectory
|
||||
? projectPath
|
||||
: Path.GetTempPath();
|
||||
|
||||
var secretManager = new Program(Console.Out, dir) { Logger = _logger };
|
||||
|
||||
var secrets = new KeyValuePair<string, string>[]
|
||||
{
|
||||
new KeyValuePair<string, string>("key1", Guid.NewGuid().ToString()),
|
||||
new KeyValuePair<string, string>("Facebook:AppId", Guid.NewGuid().ToString()),
|
||||
new KeyValuePair<string, string>(@"key-@\/.~123!#$%^&*())-+==", @"key-@\/.~123!#$%^&*())-+=="),
|
||||
new KeyValuePair<string, string>("key2", string.Empty)
|
||||
};
|
||||
|
||||
foreach (var secret in secrets)
|
||||
{
|
||||
var parameters = fromCurrentDirectory ?
|
||||
new string[] { "set", secret.Key, secret.Value } :
|
||||
new string[] { "set", secret.Key, secret.Value, "-p", projectPath };
|
||||
secretManager.RunInternal(parameters);
|
||||
}
|
||||
|
||||
Assert.Equal(4, _logger.Messages.Count);
|
||||
|
||||
foreach (var keyValue in secrets)
|
||||
{
|
||||
Assert.Contains(
|
||||
string.Format("Successfully saved {0} = {1} to the secret store.", keyValue.Key, keyValue.Value),
|
||||
_logger.Messages);
|
||||
}
|
||||
|
||||
// Verify secrets are persisted.
|
||||
_logger.Messages.Clear();
|
||||
var args = fromCurrentDirectory ?
|
||||
new string[] { "list" } :
|
||||
new string[] { "list", "-p", projectPath };
|
||||
secretManager.RunInternal(args);
|
||||
Assert.Equal(4, _logger.Messages.Count);
|
||||
foreach (var keyValue in secrets)
|
||||
{
|
||||
Assert.Contains(
|
||||
string.Format("{0} = {1}", keyValue.Key, keyValue.Value),
|
||||
_logger.Messages);
|
||||
}
|
||||
|
||||
// Clear secrets.
|
||||
_logger.Messages.Clear();
|
||||
args = fromCurrentDirectory ? new string[] { "clear" } : new string[] { "clear", "-p", projectPath };
|
||||
secretManager.RunInternal(args);
|
||||
Assert.Equal(0, _logger.Messages.Count);
|
||||
|
||||
args = fromCurrentDirectory ? new string[] { "list" } : new string[] { "list", "-p", projectPath };
|
||||
secretManager.RunInternal(args);
|
||||
Assert.Equal(1, _logger.Messages.Count);
|
||||
Assert.Contains(Resources.Error_No_Secrets_Found, _logger.Messages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// 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 Microsoft.Extensions.Logging;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
||||
{
|
||||
public class TestLogger : ILogger
|
||||
{
|
||||
private CommandOutputProvider _commandOutputProvider;
|
||||
private readonly ILogger _wrapped;
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public TestLogger(ITestOutputHelper output = null)
|
||||
{
|
||||
_commandOutputProvider = new CommandOutputProvider();
|
||||
_wrapped = _commandOutputProvider.CreateLogger("");
|
||||
_output = output;
|
||||
}
|
||||
|
||||
public void SetLevel(LogLevel level)
|
||||
{
|
||||
_commandOutputProvider.LogLevel = LogLevel.Debug;
|
||||
}
|
||||
|
||||
public List<string> Messages { get; set; } = new List<string>();
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return _wrapped.IsEnabled(logLevel);
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (IsEnabled(logLevel))
|
||||
{
|
||||
Messages.Add(formatter(state, exception));
|
||||
_output?.WriteLine(formatter(state, exception));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// 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.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Extensions.Configuration.UserSecrets.Tests
|
||||
{
|
||||
internal class UserSecretHelper
|
||||
{
|
||||
internal static string GetTempSecretProject()
|
||||
{
|
||||
string userSecretsId;
|
||||
return GetTempSecretProject(out userSecretsId);
|
||||
}
|
||||
|
||||
internal static string GetTempSecretProject(out string userSecretsId)
|
||||
{
|
||||
var projectPath = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "usersecretstest", Guid.NewGuid().ToString()));
|
||||
userSecretsId = Guid.NewGuid().ToString();
|
||||
File.WriteAllText(
|
||||
Path.Combine(projectPath.FullName, "project.json"),
|
||||
JsonConvert.SerializeObject(new { userSecretsId }));
|
||||
return projectPath.FullName;
|
||||
}
|
||||
|
||||
internal static void SetTempSecretInProject(string projectPath, string userSecretsId)
|
||||
{
|
||||
File.WriteAllText(
|
||||
Path.Combine(projectPath, "project.json"),
|
||||
JsonConvert.SerializeObject(new { userSecretsId }));
|
||||
}
|
||||
|
||||
internal static void DeleteTempSecretProject(string projectPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(projectPath, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore failures.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"buildOptions": {
|
||||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotnet-test-xunit": "2.2.0-*",
|
||||
"Microsoft.Extensions.SecretManager.Tools": "1.0.0-*",
|
||||
"xunit": "2.2.0-*"
|
||||
},
|
||||
"testRunner": "xunit",
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"version": "1.0.0",
|
||||
"type": "platform"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue