dotnet-user-secrets: add support for json output and piping json input
This commit is contained in:
Родитель
01d35b0624
Коммит
a0e164f379
|
@ -2,11 +2,10 @@
|
|||
// 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 class ClearCommand : ICommand
|
||||
{
|
||||
public static void Configure(CommandLineApplication command, CommandLineOptions options)
|
||||
{
|
||||
|
@ -19,10 +18,10 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
});
|
||||
}
|
||||
|
||||
public void Execute(SecretsStore store, ILogger logger)
|
||||
public void Execute(CommandContext context)
|
||||
{
|
||||
store.Clear();
|
||||
store.Save();
|
||||
context.SecretStore.Clear();
|
||||
context.SecretStore.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 Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
public class CommandContext
|
||||
{
|
||||
public CommandContext(
|
||||
SecretsStore store,
|
||||
ILogger logger,
|
||||
IConsole console)
|
||||
{
|
||||
SecretStore = store;
|
||||
Logger = logger;
|
||||
Console = console;
|
||||
}
|
||||
|
||||
public IConsole Console { get; }
|
||||
public ILogger Logger { get; }
|
||||
public SecretsStore SecretStore { get; }
|
||||
}
|
||||
}
|
|
@ -1,7 +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.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
|
||||
|
@ -9,16 +8,18 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
{
|
||||
public class CommandLineOptions
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public bool IsVerbose { get; set; }
|
||||
public bool IsHelp { get; set; }
|
||||
public string Project { get; set; }
|
||||
internal ICommand Command { get; set; }
|
||||
public ICommand Command { get; set; }
|
||||
|
||||
public static CommandLineOptions Parse(string[] args, TextWriter output)
|
||||
public static CommandLineOptions Parse(string[] args, IConsole console)
|
||||
{
|
||||
var app = new CommandLineApplication()
|
||||
{
|
||||
Out = output,
|
||||
Out = console.Out,
|
||||
Error = console.Error,
|
||||
Name = "dotnet user-secrets",
|
||||
FullName = "User Secrets Manager",
|
||||
Description = "Manages user secrets"
|
||||
|
@ -33,7 +34,13 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
var optionProject = app.Option("-p|--project <PROJECT>", "Path to project, default is current directory",
|
||||
CommandOptionType.SingleValue, inherited: true);
|
||||
|
||||
// the escape hatch if project evaluation fails, or if users want to alter a secret store other than the one
|
||||
// in the current project
|
||||
var optionId = app.Option("--id", "The user secret id to use.",
|
||||
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));
|
||||
|
@ -48,6 +55,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
return null;
|
||||
}
|
||||
|
||||
options.Id = optionId.Value();
|
||||
options.IsHelp = app.IsShowingInformation;
|
||||
options.IsVerbose = optionVerbose.HasValue();
|
||||
options.Project = optionProject.Value();
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
// 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
|
||||
public interface ICommand
|
||||
{
|
||||
void Execute(SecretsStore store, ILogger logger);
|
||||
void Execute(CommandContext context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
public interface IConsole
|
||||
{
|
||||
TextWriter Out { get; }
|
||||
TextWriter Error { get; }
|
||||
TextReader In { get; }
|
||||
bool IsInputRedirected { get; }
|
||||
}
|
||||
}
|
|
@ -3,35 +3,67 @@
|
|||
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class ListCommand : ICommand
|
||||
public class ListCommand : ICommand
|
||||
{
|
||||
private readonly bool _jsonOutput;
|
||||
|
||||
public static void Configure(CommandLineApplication command, CommandLineOptions options)
|
||||
{
|
||||
command.Description = "Lists all the application secrets";
|
||||
command.HelpOption();
|
||||
|
||||
var optJson = command.Option("--json", "Use json output. JSON is wrapped by '//BEGIN' and '//END'",
|
||||
CommandOptionType.NoValue);
|
||||
|
||||
command.OnExecute(() =>
|
||||
{
|
||||
options.Command = new ListCommand();
|
||||
options.Command = new ListCommand(optJson.HasValue());
|
||||
});
|
||||
}
|
||||
|
||||
public void Execute(SecretsStore store, ILogger logger)
|
||||
public ListCommand(bool jsonOutput)
|
||||
{
|
||||
if (store.Count == 0)
|
||||
_jsonOutput = jsonOutput;
|
||||
}
|
||||
|
||||
public void Execute(CommandContext context)
|
||||
{
|
||||
if (_jsonOutput)
|
||||
{
|
||||
logger.LogInformation(Resources.Error_No_Secrets_Found);
|
||||
ReportJson(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.SecretStore.Count == 0)
|
||||
{
|
||||
context.Logger.LogInformation(Resources.Error_No_Secrets_Found);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var secret in store.AsEnumerable())
|
||||
foreach (var secret in context.SecretStore.AsEnumerable())
|
||||
{
|
||||
logger.LogInformation(Resources.FormatMessage_Secret_Value_Format(secret.Key, secret.Value));
|
||||
context.Logger.LogInformation(Resources.FormatMessage_Secret_Value_Format(secret.Key, secret.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportJson(CommandContext context)
|
||||
{
|
||||
var jObject = new JObject();
|
||||
foreach(var item in context.SecretStore.AsEnumerable())
|
||||
{
|
||||
jObject[item.Key] = item.Value;
|
||||
}
|
||||
|
||||
// TODO logger would prefix each line.
|
||||
context.Console.Out.WriteLine("//BEGIN");
|
||||
context.Console.Out.WriteLine(jObject.ToString(Formatting.Indented));
|
||||
context.Console.Out.WriteLine("//END");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
public class PhysicalConsole : IConsole
|
||||
{
|
||||
private PhysicalConsole() { }
|
||||
|
||||
public static IConsole Singleton { get; } = new PhysicalConsole();
|
||||
public TextWriter Error => Console.Error;
|
||||
public TextReader In => Console.In;
|
||||
public TextWriter Out => Console.Out;
|
||||
public bool IsInputRedirected => Console.IsInputRedirected;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.Extensions.Configuration.Json;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
public class ReadableJsonConfigurationProvider : JsonConfigurationProvider
|
||||
{
|
||||
public ReadableJsonConfigurationProvider()
|
||||
: base(new JsonConfigurationSource())
|
||||
{
|
||||
}
|
||||
|
||||
public IDictionary<string, string> CurrentData => Data;
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging;
|
|||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class RemoveCommand : ICommand
|
||||
public class RemoveCommand : ICommand
|
||||
{
|
||||
private readonly string _keyName;
|
||||
|
||||
|
@ -33,16 +33,16 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
_keyName = keyName;
|
||||
}
|
||||
|
||||
public void Execute(SecretsStore store, ILogger logger)
|
||||
public void Execute(CommandContext context)
|
||||
{
|
||||
if (!store.ContainsKey(_keyName))
|
||||
if (!context.SecretStore.ContainsKey(_keyName))
|
||||
{
|
||||
logger.LogWarning(Resources.Error_Missing_Secret, _keyName);
|
||||
context.Logger.LogWarning(Resources.Error_Missing_Secret, _keyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
store.Remove(_keyName);
|
||||
store.Save();
|
||||
context.SecretStore.Remove(_keyName);
|
||||
context.SecretStore.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class SecretsStore
|
||||
public class SecretsStore
|
||||
{
|
||||
private readonly string _secretsFilePath;
|
||||
private IDictionary<string, string> _secrets;
|
||||
|
@ -27,13 +27,15 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
|
||||
_secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId);
|
||||
logger.LogDebug(Resources.Message_Secret_File_Path, _secretsFilePath);
|
||||
_secrets = Load(userSecretsId);
|
||||
}
|
||||
|
||||
_secrets = new ConfigurationBuilder()
|
||||
.AddJsonFile(_secretsFilePath, optional: true)
|
||||
.Build()
|
||||
.AsEnumerable()
|
||||
.Where(i => i.Value != null)
|
||||
.ToDictionary(i => i.Key, i => i.Value, StringComparer.OrdinalIgnoreCase);
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _secrets[key];
|
||||
}
|
||||
}
|
||||
|
||||
public int Count => _secrets.Count;
|
||||
|
@ -54,7 +56,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
public virtual void Save()
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(_secretsFilePath));
|
||||
|
||||
|
@ -69,5 +71,15 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
|
||||
File.WriteAllText(_secretsFilePath, contents.ToString(), Encoding.UTF8);
|
||||
}
|
||||
|
||||
protected virtual IDictionary<string, string> Load(string userSecretsId)
|
||||
{
|
||||
return new ConfigurationBuilder()
|
||||
.AddJsonFile(_secretsFilePath, optional: true)
|
||||
.Build()
|
||||
.AsEnumerable()
|
||||
.Where(i => i.Value != null)
|
||||
.ToDictionary(i => i.Key, i => i.Value, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
||||
{
|
||||
internal class SetCommand : ICommand
|
||||
public class SetCommand : ICommand
|
||||
{
|
||||
private readonly string _keyName;
|
||||
private readonly string _keyValue;
|
||||
|
@ -14,38 +17,88 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
|
|||
public static void Configure(CommandLineApplication command, CommandLineOptions options)
|
||||
{
|
||||
command.Description = "Sets the user secret to the specified value";
|
||||
command.ExtendedHelpText = @"
|
||||
Additional Info:
|
||||
This command will also handle piped input. Piped input is expected to be a valid JSON format.
|
||||
|
||||
Examples:
|
||||
dotnet user-secrets set ConnStr ""User ID=bob;Password=***""
|
||||
cat secrets.json | dotnet user-secrets set
|
||||
";
|
||||
|
||||
command.HelpOption();
|
||||
|
||||
var keyArg = command.Argument("[name]", "Name of the secret");
|
||||
var nameArg = 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);
|
||||
options.Command = new SetCommand(nameArg.Value, valueArg.Value);
|
||||
});
|
||||
}
|
||||
|
||||
public SetCommand(string keyName, string keyValue)
|
||||
internal SetCommand(string keyName, string keyValue)
|
||||
{
|
||||
Debug.Assert(keyName != null || keyValue == null, "Inconsistent state. keyValue must not be null if keyName is null.");
|
||||
_keyName = keyName;
|
||||
_keyValue = keyValue;
|
||||
}
|
||||
|
||||
public void Execute(SecretsStore store, ILogger logger)
|
||||
internal SetCommand()
|
||||
{ }
|
||||
|
||||
public void Execute(CommandContext context)
|
||||
{
|
||||
store.Set(_keyName, _keyValue);
|
||||
store.Save();
|
||||
logger.LogInformation(Resources.Message_Saved_Secret, _keyName, _keyValue);
|
||||
if (context.Console.IsInputRedirected && _keyName == null)
|
||||
{
|
||||
ReadFromInput(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFromArguments(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadFromInput(CommandContext context)
|
||||
{
|
||||
// parses stdin with the same parser that Microsoft.Extensions.Configuration.Json would use
|
||||
var provider = new ReadableJsonConfigurationProvider();
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
using (var writer = new StreamWriter(stream, Encoding.Unicode, 1024, true))
|
||||
{
|
||||
writer.Write(context.Console.In.ReadToEnd()); // TODO buffer?
|
||||
}
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
provider.Load(stream);
|
||||
}
|
||||
|
||||
foreach (var k in provider.CurrentData)
|
||||
{
|
||||
context.SecretStore.Set(k.Key, k.Value);
|
||||
}
|
||||
|
||||
context.Logger.LogInformation(Resources.Message_Saved_Secrets, provider.CurrentData.Count);
|
||||
|
||||
context.SecretStore.Save();
|
||||
}
|
||||
|
||||
private void SetFromArguments(CommandContext context)
|
||||
{
|
||||
if (_keyName == null)
|
||||
{
|
||||
throw new GracefulException(Resources.FormatError_MissingArgument("name"));
|
||||
}
|
||||
|
||||
if (_keyValue == null)
|
||||
{
|
||||
throw new GracefulException((Resources.FormatError_MissingArgument("value")));
|
||||
}
|
||||
|
||||
context.SecretStore.Set(_keyName, _keyValue);
|
||||
context.SecretStore.Save();
|
||||
context.Logger.LogInformation(Resources.Message_Saved_Secret, _keyName, _keyValue);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,17 +18,17 @@ namespace Microsoft.Extensions.SecretManager.Tools
|
|||
{
|
||||
private ILogger _logger;
|
||||
private CommandOutputProvider _loggerProvider;
|
||||
private readonly TextWriter _consoleOutput;
|
||||
private readonly IConsole _console;
|
||||
private readonly string _workingDirectory;
|
||||
|
||||
public Program()
|
||||
: this(Console.Out, Directory.GetCurrentDirectory())
|
||||
: this(PhysicalConsole.Singleton, Directory.GetCurrentDirectory())
|
||||
{
|
||||
}
|
||||
|
||||
internal Program(TextWriter consoleOutput, string workingDirectory)
|
||||
internal Program(IConsole console, string workingDirectory)
|
||||
{
|
||||
_consoleOutput = consoleOutput;
|
||||
_console = console;
|
||||
_workingDirectory = workingDirectory;
|
||||
|
||||
var loggerFactory = new LoggerFactory();
|
||||
|
@ -117,7 +117,7 @@ namespace Microsoft.Extensions.SecretManager.Tools
|
|||
|
||||
internal int RunInternal(params string[] args)
|
||||
{
|
||||
var options = CommandLineOptions.Parse(args, _consoleOutput);
|
||||
var options = CommandLineOptions.Parse(args, _console);
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
|
@ -136,12 +136,18 @@ namespace Microsoft.Extensions.SecretManager.Tools
|
|||
|
||||
var userSecretsId = ResolveUserSecretsId(options);
|
||||
var store = new SecretsStore(userSecretsId, Logger);
|
||||
options.Command.Execute(store, Logger);
|
||||
var context = new CommandContext(store, Logger, _console);
|
||||
options.Command.Execute(context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private string ResolveUserSecretsId(CommandLineOptions options)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(options.Id))
|
||||
{
|
||||
return options.Id;
|
||||
}
|
||||
|
||||
var projectPath = options.Project ?? _workingDirectory;
|
||||
|
||||
if (!Path.IsPathRooted(projectPath))
|
||||
|
|
|
@ -26,6 +26,24 @@ namespace Microsoft.Extensions.SecretManager.Tools
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_Command_Failed", "message"), message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Missing parameter value for '{name}'.
|
||||
/// Use the '--help' flag to see info.
|
||||
/// </summary>
|
||||
internal static string Error_MissingArgument
|
||||
{
|
||||
get { return GetString("Error_MissingArgument"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Missing parameter value for '{name}'.
|
||||
/// Use the '--help' flag to see info.
|
||||
/// </summary>
|
||||
internal static string FormatError_MissingArgument(object name)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_MissingArgument", "name"), name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot find '{key}' in the secret store.
|
||||
/// </summary>
|
||||
|
@ -106,6 +124,22 @@ namespace Microsoft.Extensions.SecretManager.Tools
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("Message_Saved_Secret", "key", "value"), key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Successfully saved {number} secrets to the secret store.
|
||||
/// </summary>
|
||||
internal static string Message_Saved_Secrets
|
||||
{
|
||||
get { return GetString("Message_Saved_Secrets"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Successfully saved {number} secrets to the secret store.
|
||||
/// </summary>
|
||||
internal static string FormatMessage_Saved_Secrets(object number)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Message_Saved_Secrets", "number"), number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Secrets file path {secretsFilePath}.
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
dotnet-user-secrets
|
||||
===================
|
||||
|
||||
`dotnet-user-secrets` is a command line tool for managing the secrets in a user secret store.
|
||||
|
||||
### How To Install
|
||||
|
||||
Add `Microsoft.Extensions.SecretManager.Tools` to the `tools` section of your `project.json` file:
|
||||
|
||||
```js
|
||||
{
|
||||
..
|
||||
"tools": {
|
||||
"Microsoft.Extensions.SecretManager.Tools": "1.0.0-*"
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### How To Use
|
||||
|
||||
Run `dotnet user-secrets --help` for more information about usage.
|
|
@ -120,6 +120,10 @@
|
|||
<data name="Error_Command_Failed" xml:space="preserve">
|
||||
<value>Command failed : {message}</value>
|
||||
</data>
|
||||
<data name="Error_MissingArgument" xml:space="preserve">
|
||||
<value>Missing parameter value for '{name}'.
|
||||
Use the '--help' flag to see info.</value>
|
||||
</data>
|
||||
<data name="Error_Missing_Secret" xml:space="preserve">
|
||||
<value>Cannot find '{key}' in the secret store.</value>
|
||||
</data>
|
||||
|
@ -135,6 +139,9 @@
|
|||
<data name="Message_Saved_Secret" xml:space="preserve">
|
||||
<value>Successfully saved {key} = {value} to the secret store.</value>
|
||||
</data>
|
||||
<data name="Message_Saved_Secrets" xml:space="preserve">
|
||||
<value>Successfully saved {number} secrets to the secret store.</value>
|
||||
</data>
|
||||
<data name="Message_Secret_File_Path" xml:space="preserve">
|
||||
<value>Secrets file path {secretsFilePath}.</value>
|
||||
</data>
|
||||
|
|
|
@ -8,9 +8,9 @@ using System.Text;
|
|||
using Microsoft.Extensions.Configuration.UserSecrets;
|
||||
using Microsoft.Extensions.Configuration.UserSecrets.Tests;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.SecretManager.Tools.Internal;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Microsoft.Extensions.SecretManager.Tools.Internal;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
||||
{
|
||||
|
@ -48,7 +48,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
|||
public void Error_Project_DoesNotExist()
|
||||
{
|
||||
var projectPath = Path.Combine(GetTempSecretProject(), "does_not_exist", "project.json");
|
||||
var secretManager = new Program(Console.Out, Directory.GetCurrentDirectory()) { Logger = _logger };
|
||||
var secretManager = new Program(new TestConsole(), Directory.GetCurrentDirectory()) { Logger = _logger };
|
||||
|
||||
var ex = Assert.Throws<GracefulException>(() => secretManager.RunInternal("list", "--project", projectPath));
|
||||
|
||||
|
@ -61,7 +61,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
|||
var projectPath = GetTempSecretProject();
|
||||
var cwd = Path.Combine(projectPath, "nested1");
|
||||
Directory.CreateDirectory(cwd);
|
||||
var secretManager = new Program(Console.Out, cwd) { Logger = _logger, CommandOutputProvider = _logger.CommandOutputProvider };
|
||||
var secretManager = new Program(new TestConsole(), cwd) { Logger = _logger, CommandOutputProvider = _logger.CommandOutputProvider };
|
||||
secretManager.CommandOutputProvider.LogLevel = LogLevel.Debug;
|
||||
|
||||
secretManager.RunInternal("list", "-p", "../", "--verbose");
|
||||
|
@ -86,7 +86,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
|||
var dir = fromCurrentDirectory
|
||||
? projectPath
|
||||
: Path.GetTempPath();
|
||||
var secretManager = new Program(Console.Out, dir) { Logger = _logger };
|
||||
var secretManager = new Program(new TestConsole(), dir) { Logger = _logger };
|
||||
|
||||
foreach (var secret in secrets)
|
||||
{
|
||||
|
@ -222,6 +222,27 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
|||
Assert.Contains("AzureAd:ClientSecret = abcd郩˙î", _logger.Messages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void List_Json()
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
var testConsole = new TestConsole
|
||||
{
|
||||
Out = new StringWriter(output)
|
||||
};
|
||||
string id;
|
||||
var projectPath = GetTempSecretProject(out id);
|
||||
var secretsFile = PathHelper.GetSecretsPathFromSecretsId(id);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(secretsFile));
|
||||
File.WriteAllText(secretsFile, @"{ ""AzureAd"": { ""ClientSecret"": ""abcd郩˙î""} }", Encoding.UTF8);
|
||||
var secretManager = new Program(testConsole, Path.GetDirectoryName(projectPath)) { Logger = _logger };
|
||||
secretManager.RunInternal("list", "--id", id, "--json");
|
||||
var stdout = output.ToString();
|
||||
Assert.Contains("//BEGIN", stdout);
|
||||
Assert.Contains(@"""AzureAd:ClientSecret"": ""abcd郩˙î""", stdout);
|
||||
Assert.Contains("//END", stdout);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_Flattens_Nested_Objects()
|
||||
{
|
||||
|
@ -265,7 +286,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
|||
? projectPath
|
||||
: Path.GetTempPath();
|
||||
|
||||
var secretManager = new Program(Console.Out, dir) { Logger = _logger };
|
||||
var secretManager = new Program(new TestConsole(), dir) { Logger = _logger };
|
||||
|
||||
var secrets = new KeyValuePair<string, string>[]
|
||||
{
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.SecretManager.Tools.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
||||
{
|
||||
public class SetCommandTest
|
||||
{
|
||||
[Fact]
|
||||
public void SetsFromPipedInput()
|
||||
{
|
||||
var input = @"
|
||||
{
|
||||
""Key1"": ""str value"",
|
||||
""Key2"": 1234,
|
||||
""Key3"": false
|
||||
}";
|
||||
var testConsole = new TestConsole
|
||||
{
|
||||
IsInputRedirected = true,
|
||||
In = new StringReader(input)
|
||||
};
|
||||
var secretStore = new TestSecretsStore();
|
||||
var command = new SetCommand();
|
||||
|
||||
command.Execute(new CommandContext(secretStore, NullLogger.Instance, testConsole));
|
||||
|
||||
Assert.Equal(3, secretStore.Count);
|
||||
Assert.Equal("str value", secretStore["Key1"]);
|
||||
Assert.Equal("1234", secretStore["Key2"]);
|
||||
Assert.Equal("False", secretStore["Key3"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNestedObjects()
|
||||
{
|
||||
var input = @"
|
||||
{
|
||||
""Key1"": {
|
||||
""nested"" : ""value""
|
||||
},
|
||||
""array"": [ 1, 2 ]
|
||||
}";
|
||||
|
||||
var testConsole = new TestConsole
|
||||
{
|
||||
IsInputRedirected = true,
|
||||
In = new StringReader(input)
|
||||
};
|
||||
var secretStore = new TestSecretsStore();
|
||||
var command = new SetCommand();
|
||||
|
||||
command.Execute(new CommandContext(secretStore, NullLogger.Instance, testConsole));
|
||||
|
||||
Assert.Equal(3, secretStore.Count);
|
||||
Assert.True(secretStore.ContainsKey("Key1:nested"));
|
||||
Assert.Equal("value", secretStore["Key1:nested"]);
|
||||
Assert.Equal("1", secretStore["array:0"]);
|
||||
Assert.Equal("2", secretStore["array:1"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnlyPipesInIfNoArgs()
|
||||
{
|
||||
var testConsole = new TestConsole
|
||||
{
|
||||
IsInputRedirected = true,
|
||||
In = new StringReader("")
|
||||
};
|
||||
var secretStore = new TestSecretsStore();
|
||||
var command = new SetCommand("key", null);
|
||||
|
||||
var ex = Assert.Throws<GracefulException>(
|
||||
() => command.Execute(new CommandContext(secretStore, NullLogger.Instance, testConsole)));
|
||||
Assert.Equal(Resources.FormatError_MissingArgument("value"), ex.Message);
|
||||
}
|
||||
|
||||
private class TestSecretsStore : SecretsStore
|
||||
{
|
||||
public TestSecretsStore()
|
||||
: base("xyz", NullLogger.Instance)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IDictionary<string, string> Load(string userSecretsId)
|
||||
{
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public override void Save()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// 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 Microsoft.Extensions.SecretManager.Tools.Internal;
|
||||
|
||||
namespace Microsoft.Extensions.SecretManager.Tools.Tests
|
||||
{
|
||||
public class TestConsole : IConsole
|
||||
{
|
||||
public TextWriter Error { get; set; } = Console.Error;
|
||||
public TextReader In { get; set; } = Console.In;
|
||||
public TextWriter Out { get; set; } = Console.Out;
|
||||
public bool IsInputRedirected { get; set; } = false;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace Microsoft.Extensions.Configuration.UserSecrets.Tests
|
||||
{
|
||||
internal class UserSecretHelper
|
||||
public class UserSecretHelper
|
||||
{
|
||||
internal static string GetTempSecretProject()
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче