Adding user secrets to configuration repo

This commit is contained in:
Praburaj 2015-03-20 12:31:30 -07:00
Родитель e39d2d2d7d
Коммит 7a4af7df7c
21 изменённых файлов: 1484 добавлений и 1 удалений

Просмотреть файл

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22609.0
VisualStudioVersion = 14.0.22710.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}"
EndProject
@ -27,6 +27,12 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Configu
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.ConfigurationModel.Test.Common", "test\Microsoft.Framework.ConfigurationModel.Test.Common\Microsoft.Framework.ConfigurationModel.Test.Common.xproj", "{29C120E5-F682-4BFB-826B-040A594802CA}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.ConfigurationModel.UserSecrets", "src\Microsoft.Framework.ConfigurationModel.UserSecrets\Microsoft.Framework.ConfigurationModel.UserSecrets.xproj", "{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SecretManager", "src\SecretManager\SecretManager.xproj", "{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SecretManager.Tests", "test\SecretManager.Tests\SecretManager.Tests.xproj", "{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -141,6 +147,42 @@ Global
{29C120E5-F682-4BFB-826B-040A594802CA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{29C120E5-F682-4BFB-826B-040A594802CA}.Release|x86.ActiveCfg = Release|Any CPU
{29C120E5-F682-4BFB-826B-040A594802CA}.Release|x86.Build.0 = Release|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Debug|x86.ActiveCfg = Debug|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Debug|x86.Build.0 = Debug|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Release|Any CPU.Build.0 = Release|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Release|x86.ActiveCfg = Release|Any CPU
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Release|x86.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}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Debug|x86.ActiveCfg = Debug|Any CPU
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Debug|x86.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
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Release|x86.ActiveCfg = Release|Any CPU
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.Release|x86.Build.0 = Release|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Debug|x86.ActiveCfg = Debug|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Debug|x86.Build.0 = Debug|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Release|Any CPU.Build.0 = Release|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Release|x86.ActiveCfg = Release|Any CPU
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -156,5 +198,8 @@ Global
{8967162D-4966-40A7-9970-395A206732AC} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}
{3F1CB08E-9FBD-4CAE-A78A-4AC43F24FC49} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}
{29C120E5-F682-4BFB-826B-040A594802CA} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF}
{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}
{113EBBD2-E857-4CAF-9B53-7A8742CBCD4A} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.Framework.ConfigurationModel.UserSecrets;
using Microsoft.Framework.Internal;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Runtime.Infrastructure;
namespace Microsoft.Framework.ConfigurationModel
{
public static class ConfigurationExtensions
{
/// <summary>
/// Adds the user secrets configuration source.
/// </summary>
/// <param name="configuration"></param>
/// <returns></returns>
public static IConfigurationSourceRoot AddUserSecrets([NotNull]this IConfigurationSourceRoot configuration)
{
var appEnv = (IApplicationEnvironment)CallContextServiceLocator.Locator.ServiceProvider.GetService(typeof(IApplicationEnvironment));
var secretPath = PathHelper.GetSecretsPath(appEnv.ApplicationBasePath);
if (!File.Exists(secretPath))
{
// TODO: Use the optional config add after that's available?.
return configuration;
}
return configuration.AddJsonFile(secretPath);
}
/// <summary>
/// Adds the user secrets configuration source with specified secrets id.
/// </summary>
/// <param name="configuration"></param>
/// <returns></returns>
public static IConfigurationSourceRoot AddUserSecrets([NotNull]this IConfigurationSourceRoot configuration, [NotNull]string userSecretsId)
{
var secretPath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId);
if (!File.Exists(secretPath))
{
// TODO: Use the optional config add after that's available?.
return configuration;
}
return configuration.AddJsonFile(secretPath);
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" 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)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>58b6443b-1278-4df9-b7bb-ddf3bffcf868</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Просмотреть файл

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
using Newtonsoft.Json.Linq;
namespace Microsoft.Framework.ConfigurationModel.UserSecrets
{
public class PathHelper
{
private const string Secrets_File_Name = "secrets.json";
public static string GetSecretsPath([NotNull]string projectPath)
{
var projectFilePath = Path.Combine(projectPath, "project.json");
if (!File.Exists(projectFilePath))
{
throw new InvalidOperationException(
string.Format(Resources.Error_Missing_Project_Json, projectFilePath));
}
var obj = JObject.Parse(File.ReadAllText(projectFilePath));
var userSecretsId = obj.Value<string>("userSecretsId");
if (string.IsNullOrEmpty(userSecretsId))
{
throw new InvalidOperationException(
string.Format(Resources.Error_Missing_UserSecretId_In_Project_Json, projectFilePath));
}
return GetSecretsPathFromSecretsId(userSecretsId);
}
public static string GetSecretsPathFromSecretsId([NotNull]string userSecretsId)
{
var badCharIndex = userSecretsId.IndexOfAny(Path.GetInvalidPathChars());
if (badCharIndex != -1)
{
throw new InvalidOperationException(
string.Format(
Resources.Error_Invalid_Character_In_UserSecrets_Id,
userSecretsId[badCharIndex],
badCharIndex));
}
var root = Environment.GetEnvironmentVariable("APPDATA") ?? // On Windows it goes to %APPDATA%\Microsoft\UserSecrets\
Environment.GetEnvironmentVariable("HOME"); // On Mac/Linux it goes to ~/.microsoft/usersecrets/
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPDATA")))
{
return Path.Combine(root, "Microsoft", "UserSecrets", userSecretsId, Secrets_File_Name);
}
else
{
return Path.Combine(root, ".microsoft", "usersecrets", userSecretsId, Secrets_File_Name);
}
}
}
}

Просмотреть файл

@ -0,0 +1,78 @@
// <auto-generated />
namespace Microsoft.Framework.ConfigurationModel.UserSecrets
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.Framework.ConfigurationModel.UserSecrets.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// Invalid character '{0}' found in 'userSecretsId' value at index '{1}'.
/// </summary>
internal static string Error_Invalid_Character_In_UserSecrets_Id
{
get { return GetString("Error_Invalid_Character_In_UserSecrets_Id"); }
}
/// <summary>
/// Invalid character '{0}' found in 'userSecretsId' value at index '{1}'.
/// </summary>
internal static string FormatError_Invalid_Character_In_UserSecrets_Id(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Error_Invalid_Character_In_UserSecrets_Id"), p0, p1);
}
/// <summary>
/// Unable to locate a project.json at '{0}'.
/// </summary>
internal static string Error_Missing_Project_Json
{
get { return GetString("Error_Missing_Project_Json"); }
}
/// <summary>
/// Unable to locate a project.json at '{0}'.
/// </summary>
internal static string FormatError_Missing_Project_Json(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Error_Missing_Project_Json"), p0);
}
/// <summary>
/// Missing 'userSecretsId' in '{0}'.
/// </summary>
internal static string Error_Missing_UserSecretId_In_Project_Json
{
get { return GetString("Error_Missing_UserSecretId_In_Project_Json"); }
}
/// <summary>
/// Missing 'userSecretsId' in '{0}'.
/// </summary>
internal static string FormatError_Missing_UserSecretId_In_Project_Json(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Error_Missing_UserSecretId_In_Project_Json"), p0);
}
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,129 @@
<?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_Invalid_Character_In_UserSecrets_Id" xml:space="preserve">
<value>Invalid character '{0}' found in 'userSecretsId' value at index '{1}'.</value>
</data>
<data name="Error_Missing_Project_Json" xml:space="preserve">
<value>Unable to locate a project.json at '{0}'.</value>
</data>
<data name="Error_Missing_UserSecretId_In_Project_Json" xml:space="preserve">
<value>Missing 'userSecretsId' in '{0}'.</value>
</data>
</root>

Просмотреть файл

@ -0,0 +1,16 @@
{
"version": "1.0.0-*",
"description": "ASP.NET 5 Configuration extensions to load user secrets.",
"dependencies": {
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }
},
"frameworks": {
"dnx451": { },
"dnxcore50": {
"dependencies": {
"System.Runtime": "4.0.20-beta-*"
}
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Logging;
using Microsoft.Framework.Runtime.Common.CommandLine;
namespace SecretManager
{
/// <summary>
/// Logger to print formatted command output.
/// </summary>
public class CommandOutputLogger : ILogger
{
private readonly CommandOutputProvider _provider;
public CommandOutputLogger(CommandOutputProvider commandOutputProvider)
{
_provider = commandOutputProvider;
}
public IDisposable BeginScope(object state)
{
throw new NotImplementedException();
}
public bool IsEnabled(LogLevel logLevel)
{
if (logLevel < _provider.LogLevel)
{
return false;
}
return true;
}
public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
{
if (IsEnabled(logLevel))
{
AnsiConsole.Output.WriteLine(string.Format("{0}: {1}", Caption(logLevel), formatter(state, exception)));
}
}
private string Caption(LogLevel logLevel)
{
switch (logLevel)
{
case LogLevel.Debug: return "\x1b[35mdebug\x1b[39m";
case LogLevel.Verbose: return "\x1b[35mverbose\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,17 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Logging;
namespace SecretManager
{
public class CommandOutputProvider : ILoggerProvider
{
public ILogger CreateLogger(string name)
{
return new CommandOutputLogger(this);
}
public LogLevel LogLevel { get; set; } = LogLevel.Information;
}
}

Просмотреть файл

@ -0,0 +1,225 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.ConfigurationModel.UserSecrets;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime.Common.CommandLine;
using Newtonsoft.Json.Linq;
namespace SecretManager
{
public class Program
{
private ILogger _logger;
private CommandOutputProvider _loggerProvider;
public Program()
{
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 int Main(string[] args)
{
try
{
var app = new CommandLineApplication();
app.Name = "user-secret";
app.Description = "Manages user secrets";
app.HelpOption("-?|-h|--help");
var optVerbose = app.Option("-v|--verbose", "Verbose output", CommandOptionType.NoValue);
app.Command("set", c =>
{
c.Description = "Sets the user secret to the specified value";
var optionProject = c.Option("-p|--project <PATH>", "Path to project, default is current directory", CommandOptionType.SingleValue);
var keyArg = c.Argument("[name]", "Name of the secret");
var valueArg = c.Argument("[value]", "Value of the secret");
c.HelpOption("-?|-h|--help");
c.OnExecute(() =>
{
var projectPath = optionProject.Value() ?? Directory.GetCurrentDirectory();
if (optVerbose.HasValue())
{
CommandOutputProvider.LogLevel = LogLevel.Verbose;
}
ProcessSecretFile(projectPath, secrets =>
{
secrets[keyArg.Value] = valueArg.Value;
});
Logger.LogInformation(Resources.Message_Saved_Secret, keyArg.Value, valueArg.Value);
return 0;
});
});
app.Command("remove", c =>
{
c.Description = "Removes the specified user secret";
var optionProject = c.Option("-p|--project <PATH>", "Path to project, default is current directory", CommandOptionType.SingleValue);
var keyArg = c.Argument("[name]", "Name of the secret");
c.HelpOption("-?|-h|--help");
c.OnExecute(() =>
{
var projectPath = optionProject.Value() ?? Directory.GetCurrentDirectory();
if (optVerbose.HasValue())
{
CommandOutputProvider.LogLevel = LogLevel.Verbose;
}
ProcessSecretFile(projectPath, secrets =>
{
if (secrets[keyArg.Value] == null)
{
Logger.LogWarning(Resources.Error_Missing_Secret, keyArg.Value);
}
else
{
secrets.Remove(keyArg.Value);
}
});
return 0;
});
});
app.Command("list", c =>
{
c.Description = "Lists all the application secrets";
var optionProject = c.Option("-p|--project <PATH>", "Path to project, default is current directory", CommandOptionType.SingleValue);
c.HelpOption("-?|-h|--help");
c.OnExecute(() =>
{
var projectPath = optionProject.Value() ?? Directory.GetCurrentDirectory();
if (optVerbose.HasValue())
{
CommandOutputProvider.LogLevel = LogLevel.Verbose;
}
ProcessSecretFile(projectPath, secrets =>
{
PrintAll(secrets);
},
persist: false);
return 0;
});
});
app.Command("clear", c =>
{
c.Description = "Deletes all the application secrets";
var optionProject = c.Option("-p|--project <PATH>", "Path to project, default is current directory", CommandOptionType.SingleValue);
c.HelpOption("-?|-h|--help");
c.OnExecute(() =>
{
var projectPath = optionProject.Value() ?? Directory.GetCurrentDirectory();
if (optVerbose.HasValue())
{
CommandOutputProvider.LogLevel = LogLevel.Verbose;
}
ProcessSecretFile(projectPath, secrets =>
{
secrets.RemoveAll();
});
return 0;
});
});
// Show help information if no subcommand/option was specified.
app.OnExecute(() =>
{
app.ShowHelp();
return 2;
});
return app.Execute(args);
}
catch (Exception exception)
{
Logger.LogCritical(Resources.Error_Command_Failed, exception.Message);
return 1;
}
}
private void PrintAll(JObject secrets)
{
if (secrets.Count == 0)
{
Logger.LogInformation(Resources.Error_No_Secrets_Found);
}
else
{
foreach (var secret in secrets)
{
Logger.LogInformation(Resources.Message_Secret_Value_Format, secret.Key, secret.Value);
}
}
}
private void ProcessSecretFile(string projectPath, Action<JObject> observer, bool persist = true)
{
Logger.LogVerbose(Resources.Message_Project_File_Path, projectPath);
var secretsFilePath = PathHelper.GetSecretsPath(projectPath);
Logger.LogVerbose(Resources.Message_Secret_File_Path, secretsFilePath);
var secretObj = File.Exists(secretsFilePath) ?
JObject.Parse(File.ReadAllText(secretsFilePath)) :
new JObject();
observer(secretObj);
if (persist)
{
Directory.CreateDirectory(Path.GetDirectoryName(secretsFilePath));
File.WriteAllText(secretsFilePath, secretObj.ToString());
}
}
}
}

Просмотреть файл

@ -0,0 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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("SecretManager.Tests")]

142
src/SecretManager/Properties/Resources.Designer.cs сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,142 @@
// <auto-generated />
namespace SecretManager
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("SecretManager.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,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" 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)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>8730e848-ca0f-4e0a-9a2f-bc22ad0b2c4e</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Просмотреть файл

@ -0,0 +1,22 @@
{
"version": "1.0.0-*",
"description": "ASP.NET 5 tool to manage user secrets.",
"dependencies": {
"Microsoft.Framework.ConfigurationModel.UserSecrets": "1.0.0-*",
"Microsoft.Framework.CommandLineUtils": { "version": "1.0.0-*", "type": "build" },
"Microsoft.Framework.Logging": "1.0.0-*",
"Newtonsoft.Json": "6.0.6"
},
"commands": {
"user-secret": "SecretManager"
},
"userSecretsId": "testuserSecretsId",
"frameworks": {
"dnx451": { },
"dnxcore50": {
"dependencies": {
"System.Console": "4.0.0-beta-*"
}
}
}
}

Просмотреть файл

@ -0,0 +1,77 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.ConfigurationModel.UserSecrets;
using Xunit;
namespace SecretManager.Tests
{
public class PathHelperTests
{
[Fact]
public void Gives_Correct_Secret_Path()
{
string userSecretsId;
var projectPath = UserSecretHelper.GetTempSecretProject(out userSecretsId);
var actualSecretPath = PathHelper.GetSecretsPath(projectPath);
var root = Environment.GetEnvironmentVariable("APPDATA") ?? // On Windows it goes to %APPDATA%\Microsoft\UserSecrets\
Environment.GetEnvironmentVariable("HOME"); // On Mac/Linux it goes to ~/.microsoft/usersecrets/
var expectedSecretPath = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPDATA"))?
Path.Combine(root, "Microsoft", "UserSecrets", userSecretsId, "secrets.json") :
Path.Combine(root, ".microsoft", "usersecrets", userSecretsId, "secrets.json");
Assert.Equal(expectedSecretPath, actualSecretPath);
UserSecretHelper.DeleteTempSecretProject(projectPath);
}
[Fact]
public void Throws_If_Project_Json_Not_Found()
{
var projectPath = UserSecretHelper.GetTempSecretProject();
File.Delete(Path.Combine(projectPath, "project.json"));
Assert.Throws<InvalidOperationException>(() =>
{
PathHelper.GetSecretsPath(projectPath);
});
UserSecretHelper.DeleteTempSecretProject(projectPath);
}
[Fact]
public void Throws_If_Project_Json_Does_Not_Contain_UserSecretId()
{
var projectPath = UserSecretHelper.GetTempSecretProject();
File.WriteAllText(Path.Combine(projectPath, "project.json"), "{}");
Assert.Throws<InvalidOperationException>(() =>
{
PathHelper.GetSecretsPath(projectPath);
});
UserSecretHelper.DeleteTempSecretProject(projectPath);
}
[Fact]
public void Throws_If_UserSecretId_Contains_Invalid_Characters()
{
var projectPath = UserSecretHelper.GetTempSecretProject();
foreach (var character in Path.GetInvalidPathChars())
{
UserSecretHelper.SetTempSecretInProject(projectPath, "Test" + character);
Assert.Throws<InvalidOperationException>(() =>
{
PathHelper.GetSecretsPath(projectPath);
});
}
UserSecretHelper.DeleteTempSecretProject(projectPath);
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
<?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)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>113ebbd2-e857-4caf-9b53-7a8742cbcd4a</ProjectGuid>
<RootNamespace>SecretManager.Tests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Просмотреть файл

@ -0,0 +1,247 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.Framework.ConfigurationModel.UserSecrets;
using Xunit;
namespace SecretManager.Tests
{
public class SecretManagerTests
{
[Fact]
public void SetSecret_With_ProjectPath_As_CommandLine_Parameter()
{
SetSecrets(fromCurrentDirectory: false);
}
[Fact]
public void SetSecret_From_CurrentDirectory()
{
var backUpCurrentDirectory = Directory.GetCurrentDirectory();
try
{
SetSecrets(fromCurrentDirectory: true);
}
catch (Exception)
{
Directory.SetCurrentDirectory(backUpCurrentDirectory);
throw;
}
}
private 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();
if (fromCurrentDirectory)
{
Directory.SetCurrentDirectory(projectPath); // Point current directory to the project.json directory.
}
var logger = new TestLogger();
var secretManager = new Program() { 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.Main(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.Main(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.Main(parameters);
}
// Verify secrets are removed.
logger.Messages.Clear();
args = fromCurrentDirectory ?
new string[] { "list" } : new string[] { "list", "-p", projectPath };
secretManager.Main(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 logger = new TestLogger();
var secretManager = new Program() { Logger = logger };
secretManager.Main(new string[] { "set", "secret1", "value1", "-p", projectPath });
Assert.Equal(1, logger.Messages.Count);
Assert.Contains("Successfully saved secret1 = value1 to the secret store.", logger.Messages);
secretManager.Main(new string[] { "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.Main(new string[] { "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();
var logger = new TestLogger(verbose: true);
var secretManager = new Program() { Logger = logger };
secretManager.Main(new string[] { "-v", "set", "secret1", "value1", "-p", projectPath });
Assert.Equal(3, logger.Messages.Count);
Assert.Contains(string.Format("Project file path {0}.", projectPath), 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.Main(new string[] { "-v", "list", "-p", projectPath });
Assert.Equal(3, logger.Messages.Count);
Assert.Contains(string.Format("Project file path {0}.", projectPath), 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 logger = new TestLogger();
var secretManager = new Program() { Logger = logger };
secretManager.Main(new string[] { "remove", "secret1", "-p", projectPath });
Assert.Equal(1, logger.Messages.Count);
Assert.Contains("Cannot find 'secret1' in the secret store.", logger.Messages);
}
[Fact]
public void List_Empty_Secrets_File()
{
var projectPath = UserSecretHelper.GetTempSecretProject();
var logger = new TestLogger();
var secretManager = new Program() { Logger = logger };
secretManager.Main(new string[] { "list", "-p", projectPath });
Assert.Equal(1, logger.Messages.Count);
Assert.Contains(Resources.Error_No_Secrets_Found, logger.Messages);
}
[Fact]
public void Clear_All_Secrets_With_ProjectPath_As_Parameter()
{
Clear_Secrets(fromCurrentDirectory: false);
}
[Fact]
public void Clear_All_Secrets_From_Current_Directory()
{
Clear_Secrets(fromCurrentDirectory: true);
}
private void Clear_Secrets(bool fromCurrentDirectory)
{
var projectPath = UserSecretHelper.GetTempSecretProject();
if (fromCurrentDirectory)
{
Directory.SetCurrentDirectory(projectPath); // Point current directory to the project.json directory.
}
var logger = new TestLogger();
var secretManager = new Program() { 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.Main(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.Main(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.Main(args);
Assert.Equal(0, logger.Messages.Count);
args = fromCurrentDirectory ? new string[] { "list" } : new string[] { "list", "-p", projectPath };
secretManager.Main(args);
Assert.Equal(1, logger.Messages.Count);
Assert.Contains(Resources.Error_No_Secrets_Found, logger.Messages);
}
}
}

Просмотреть файл

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Logging;
namespace SecretManager.Tests
{
public class TestLogger : ILogger
{
private readonly CommandOutputLogger _commandOutputLogger;
public TestLogger(bool verbose = false)
{
var commandOutputProvider = new CommandOutputProvider();
if (verbose)
{
commandOutputProvider.LogLevel = LogLevel.Verbose;
}
_commandOutputLogger = new CommandOutputLogger(commandOutputProvider);
}
public List<string> Messages { get; set; } = new List<string>();
public IDisposable BeginScope(object state)
{
throw new NotImplementedException();
}
public bool IsEnabled(LogLevel logLevel)
{
return _commandOutputLogger.IsEnabled(logLevel);
}
public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
{
if (IsEnabled(logLevel))
{
Messages.Add(formatter(state, exception));
}
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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 SecretManager.Tests
{
public 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(), Guid.NewGuid().ToString()));
userSecretsId = Guid.NewGuid().ToString();
File.WriteAllText(
Path.Combine(projectPath.FullName, "project.json"),
string.Format("{{\"userSecretsId\": {0}}}", JsonConvert.ToString(userSecretsId)));
return projectPath.FullName;
}
internal static void SetTempSecretInProject(string projectPath, string userSecretsId)
{
File.WriteAllText(
Path.Combine(projectPath, "project.json"),
string.Format("{{\"userSecretsId\": {0}}}", JsonConvert.ToString(userSecretsId)));
}
internal static void DeleteTempSecretProject(string projectPath)
{
try
{
Directory.Delete(projectPath, true);
}
catch (Exception)
{
// Ignore failures.
}
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
{
"dependencies": {
"SecretManager": "1.0.0-*",
"xunit.runner.aspnet": "2.0.0-aspnet-*"
},
"frameworks": {
"dnx451": { },
"dnxcore50": { }
},
"commands": {
"test": "xunit.runner.aspnet"
}
}