Create an initial implementation of the secret manager for VS (#355)

This commit is contained in:
Nate McMaster 2017-11-21 14:16:39 -08:00 committed by GitHub
parent bd725635c7
commit 4ecabacc76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 2260 additions and 12 deletions

View File

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27110.0
VisualStudioVersion = 15.0.27120.0
MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{66517987-2A5A-4330-B130-207039378FD4}"
ProjectSection(SolutionItems) = preProject
@ -20,12 +20,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
build.ps1 = build.ps1
build.sh = build.sh
CONTRIBUTING.md = CONTRIBUTING.md
build\dependencies.props = build\dependencies.props
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
LICENSE.txt = LICENSE.txt
NuGet.config = NuGet.config
NuGetPackageVerifier.json = NuGetPackageVerifier.json
README.md = README.md
build\sources.props = build\sources.props
version.props = version.props
EndProjectSection
EndProject
@ -46,46 +48,98 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Cachin
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DeveloperCertificates.Tools", "src\Microsoft.AspNetCore.DeveloperCertificates.Tools\Microsoft.AspNetCore.DeveloperCertificates.Tools.csproj", "{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DeveloperCertificates.XPlat", "src\Microsoft.AspNetCore.DeveloperCertificates.XPlat\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj", "{96E71881-1465-44F5-B4B7-DF9B370FFD02}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.SecretManager", "tooling\Microsoft.VisualStudio.SecretManager\Microsoft.VisualStudio.SecretManager.csproj", "{5E117F2E-7152-447F-BF47-59F759EEF3A7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tooling", "tooling", "{62826851-7D74-4F1E-B7D1-12553B789CD8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.SecretManager.TestExtension", "tooling\Microsoft.VisualStudio.SecretManager.TestExtension\Microsoft.VisualStudio.SecretManager.TestExtension.csproj", "{965F8820-F809-4081-9090-1AEC903F291B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DeveloperCertificates.XPlat", "src\Microsoft.AspNetCore.DeveloperCertificates.XPlat\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj", "{96E71881-1465-44F5-B4B7-DF9B370FFD02}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
DebugNoVSIX|Any CPU = DebugNoVSIX|Any CPU
Release|Any CPU = Release|Any CPU
ReleaseNoVSIX|Any CPU = ReleaseNoVSIX|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46}.Release|Any CPU.Build.0 = Release|Any CPU
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.Release|Any CPU.Build.0 = Release|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.ReleaseNoVSIX|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}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.DebugNoVSIX|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
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E}.ReleaseNoVSIX|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}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{7B331122-83B1-4F08-A119-DC846959844C}.DebugNoVSIX|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
{7B331122-83B1-4F08-A119-DC846959844C}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{7B331122-83B1-4F08-A119-DC846959844C}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Release|Any CPU.Build.0 = Release|Any CPU
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Release|Any CPU.Build.0 = Release|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}.Release|Any CPU.Build.0 = Release|Any CPU
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{5E117F2E-7152-447F-BF47-59F759EEF3A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E117F2E-7152-447F-BF47-59F759EEF3A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E117F2E-7152-447F-BF47-59F759EEF3A7}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{5E117F2E-7152-447F-BF47-59F759EEF3A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E117F2E-7152-447F-BF47-59F759EEF3A7}.Release|Any CPU.Build.0 = Release|Any CPU
{5E117F2E-7152-447F-BF47-59F759EEF3A7}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{965F8820-F809-4081-9090-1AEC903F291B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{965F8820-F809-4081-9090-1AEC903F291B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{965F8820-F809-4081-9090-1AEC903F291B}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{965F8820-F809-4081-9090-1AEC903F291B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{965F8820-F809-4081-9090-1AEC903F291B}.Release|Any CPU.Build.0 = Release|Any CPU
{965F8820-F809-4081-9090-1AEC903F291B}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{96E71881-1465-44F5-B4B7-DF9B370FFD02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96E71881-1465-44F5-B4B7-DF9B370FFD02}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96E71881-1465-44F5-B4B7-DF9B370FFD02}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{96E71881-1465-44F5-B4B7-DF9B370FFD02}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{96E71881-1465-44F5-B4B7-DF9B370FFD02}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96E71881-1465-44F5-B4B7-DF9B370FFD02}.Release|Any CPU.Build.0 = Release|Any CPU
{96E71881-1465-44F5-B4B7-DF9B370FFD02}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{96E71881-1465-44F5-B4B7-DF9B370FFD02}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -98,6 +152,8 @@ Global
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9} = {66517987-2A5A-4330-B130-207039378FD4}
{4FED5119-EE5C-4753-88A4-D61BDEB4D6C8} = {66517987-2A5A-4330-B130-207039378FD4}
{5E117F2E-7152-447F-BF47-59F759EEF3A7} = {62826851-7D74-4F1E-B7D1-12553B789CD8}
{965F8820-F809-4081-9090-1AEC903F291B} = {62826851-7D74-4F1E-B7D1-12553B789CD8}
{96E71881-1465-44F5-B4B7-DF9B370FFD02} = {66517987-2A5A-4330-B130-207039378FD4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

9
build/VSIX.props Normal file
View File

@ -0,0 +1,9 @@
<!--
Workaround for https://github.com/dotnet/project-system/issues/2129 - VS does not support using MSBuild variables for PackageReference items.
Using a variable breaks a bunch of stuff, so this file is injected into the VSIX csproj only when building on commandline.
-->
<Project ToolsVersion="15.0">
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="$(VisualStudio_NewtonsoftJsonPackageVersion)" />
</ItemGroup>
</Project>

86
build/VSIX.targets Normal file
View File

@ -0,0 +1,86 @@
<Project>
<PropertyGroup>
<BuildVSIX Condition="'$(BuildVSIX)' ==''">true</BuildVSIX>
<RestoreDependsOn Condition="'$(OS)'=='Windows_NT' AND '$(BuildVSIX)' == 'true'">$(RestoreDependsOn);RestoreVSIX</RestoreDependsOn>
<PackageDependsOn Condition="'$(OS)'=='Windows_NT' AND '$(BuildVSIX)' == 'true'">$(PackageDependsOn);PackageVSIX</PackageDependsOn>
<VSIXName>Microsoft.VisualStudio.SecretManager</VSIXName>
<VSIXProject>$(RepositoryRoot)tooling\$(VSIXName)\$(VSIXName).csproj</VSIXProject>
<VSIXOutputPath>$(BuildDir)$(VSIXName).vsix</VSIXOutputPath>
<MSBuildArtifactsDir>$(ArtifactsDir)msbuild\</MSBuildArtifactsDir>
</PropertyGroup>
<Target
Name="GenerateVSIX"
DependsOnTargets="RestoreVSIX;PackageVSIX"
Condition="'$(OS)'=='Windows_NT'" />
<!--
VisualStudioMSBuildx86Path is set by the GetToolsets target in KoreBuild if a version of VS matching the requirements in korebuild.json is found.
-->
<Target Name="RestoreVSIX" DependsOnTargets="GetToolsets">
<PropertyGroup>
<VSIXResponseFilePath>$(MSBuildArtifactsDir)vsix-restore.rsp</VSIXResponseFilePath>
</PropertyGroup>
<ItemGroup>
<MSBuildArguments Remove="@(MSBuildArguments)" />
<MSBuildArguments Include="
$(VSIXProject);
/t:Restore;
/m;
/v:m;
/p:Configuration=$(Configuration);
/p:BuildNumber=$(BuildNumber);
/p:PackageReferencePropsPath=$(MSBuildThisFileDirectory)VSIX.props;" />
<MSBuildArguments Include="/p:DotNetPackageVersionPropsPath=$(DotNetPackageVersionPropsPath)" Condition="'$(DotNetPackageVersionPropsPath)' != ''" />
<MSBuildArguments Include="/p:DotNetRestoreSourcePropsPath=$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''" />
</ItemGroup>
<MakeDir Directories="$(MSBuildArtifactsDir)" />
<WriteLinesToFile
File="$(VSIXResponseFilePath)"
Lines="@(MSBuildArguments)"
Overwrite="true" />
<Exec Command="&quot;$(VisualStudioMSBuildx86Path)&quot; @&quot;$(VSIXResponseFilePath)&quot;"
Condition="'$(VisualStudioMSBuildx86Path)' != ''" />
</Target>
<Target Name="PackageVSIX" DependsOnTargets="GetToolsets">
<Error Text="Could not find a version of Visual Studio that has the Visual Studio SDK installed. This is required to build the SecretManager VSIX."
Condition="'$(VisualStudioMSBuildx86Path)' == ''" />
<PropertyGroup>
<VSIXLogFilePath>$(MSBuildArtifactsDir)vsix.log</VSIXLogFilePath>
<VSIXResponseFilePath>$(MSBuildArtifactsDir)vsix-build.rsp</VSIXResponseFilePath>
</PropertyGroup>
<ItemGroup>
<MSBuildArguments Remove="@(MSBuildArguments)" />
<MSBuildArguments Include="
$(VSIXProject);
/m;
/v:M;
/fl;
/flp:LogFile=$(VSIXLogFilePath);
/p:DeployExtension=false;
/p:TargetVSIXContainer=$(VSIXOutputPath);
/p:Configuration=$(Configuration);
/p:PackageReferencePropsPath=$(MSBuildThisFileDirectory)VSIX.props;" />
</ItemGroup>
<MakeDir Directories="$(MSBuildArtifactsDir)" />
<WriteLinesToFile
File="$(VSIXResponseFilePath)"
Lines="@(MSBuildArguments)"
Overwrite="true" />
<Exec Command="&quot;$(VisualStudioMSBuildx86Path)&quot; @&quot;$(VSIXResponseFilePath)&quot;"
Condition="'$(VisualStudioMSBuildx86Path)' != ''" />
</Target>
</Project>

View File

@ -3,17 +3,18 @@
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>
<PropertyGroup Label="Package Versions">
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview1-15549</InternalAspNetCoreSdkPackageVersion>
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.0-preview1-27595</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview1-27595</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview1-27595</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>2.1.0-preview1-27595</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsProcessSourcesPackageVersion>2.1.0-preview1-27595</MicrosoftExtensionsProcessSourcesPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview1-15573</InternalAspNetCoreSdkPackageVersion>
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.0-preview1-27644</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview1-27644</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview1-27644</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>2.1.0-preview1-27644</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsProcessSourcesPackageVersion>2.1.0-preview1-27644</MicrosoftExtensionsProcessSourcesPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview1-25907-02</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.3.0</MicrosoftNETTestSdkPackageVersion>
<SystemDataSqlClientPackageVersion>4.4.0</SystemDataSqlClientPackageVersion>
<SystemSecurityCryptographyCngPackageVersion>4.4.0</SystemSecurityCryptographyCngPackageVersion>
<VisualStudio_NewtonsoftJsonPackageVersion>9.0.1</VisualStudio_NewtonsoftJsonPackageVersion>
<XunitPackageVersion>2.3.0</XunitPackageVersion>
<XunitRunnerVisualStudioPackageVersion>2.3.0</XunitRunnerVisualStudioPackageVersion>
</PropertyGroup>

10
build/repo.targets Normal file
View File

@ -0,0 +1,10 @@
<Project>
<Import Project="VSIX.targets" />
<ItemGroup>
<Solutions Update="$(RepositoryRoot)DotNetTools.sln">
<!-- the 'DebugNoVSIX' and 'ReleaseNoVSIX' configurations exclude the VSIX project, which doesn't build with Microsoft.NET.Sdk yet. -->
<AdditionalProperties>Configuration=$(Configuration)NoVSIX</AdditionalProperties>
</Solutions>
</ItemGroup>
</Project>

View File

@ -1,4 +1,4 @@
<Project>
<Project>
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
<PropertyGroup Label="RestoreSources">
@ -7,6 +7,7 @@
$(RestoreSources);
https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json;
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
https://vside.myget.org/F/vssdk/api/v3/index.json;
</RestoreSources>
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true'">
$(RestoreSources);

View File

@ -1,2 +1,2 @@
version:2.1.0-preview1-15549
commithash:f570e08585fec510dd60cd4bfe8795388b757a95
version:2.1.0-preview1-15573
commithash:82cb53a8578610baf96ef33142797c68bae68c81

View File

@ -1,4 +1,16 @@
{
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/dev/tools/korebuild.schema.json",
"channel": "dev"
"channel": "dev",
"toolsets": {
"visualstudio": {
"required": [
"windows"
],
"includePrerelease": true,
"minVersion": "15.4",
"requiredWorkloads": [
"Microsoft.VisualStudio.Component.VSSDK"
]
}
}
}

View File

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ImportDirectoryBuildProps>False</ImportDirectoryBuildProps>
<ImportDirectoryBuildTargets>False</ImportDirectoryBuildTargets>
<GenerateUserSecretsAttribute>False</GenerateUserSecretsAttribute>
<UseCodebase>True</UseCodebase>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\build\Key.snk</AssemblyOriginatorKeyFile>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<Import Project="..\..\version.props" />
<Import Project="..\..\build\dependencies.props" />
<Import Project="..\..\build\sources.props" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<ProjectGuid>{965F8820-F809-4081-9090-1AEC903F291B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.VisualStudio.SecretManager.TestExtension</RootNamespace>
<AssemblyName>Microsoft.VisualStudio.SecretManager.TestExtension</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<GeneratePkgDefFile>true</GeneratePkgDefFile>
<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
<IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>true</IncludeDebugSymbolsInLocalVSIXDeployment>
<CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
<StartAction>Program</StartAction>
<StartProgram Condition="'$(DevEnvDir)' != ''">$(DevEnvDir)devenv.exe</StartProgram>
<StartArguments>/rootsuffix Exp</StartArguments>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="NotifyPropertyChanged.cs" />
<Compile Include="ProjectViewModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RelayCommand.cs" />
<Compile Include="SecretManagerTestWindow.cs" />
<Compile Include="SecretManagerTestCommand.cs" />
<Compile Include="SecretManagerTestControl.xaml.cs">
<DependentUpon>SecretManagerTestControl.xaml</DependentUpon>
</Compile>
<Compile Include="SecretManagerTestPackage.cs" />
<Compile Include="SecretManagerViewModel.cs" />
</ItemGroup>
<ItemGroup>
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Key.snk" />
</ItemGroup>
<ItemGroup>
<Page Include="SecretManagerTestControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Data" />
<Reference Include="System.Design" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.ComponentModelHost" Version="15.0.26606" />
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.Analyzers" Version="15.3.224" />
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.SDK.Tools" Version="15.3.224" />
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.SDK" Version="15.3.224" />
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem" Version="15.3.224" />
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop">
<Version>7.10.6071</Version>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.10.0" Version="10.0.30319" />
<PackageReference Include="Microsoft.VisualStudio.Shell.15.0" Version="15.0.26606" />
<PackageReference Include="Microsoft.VisualStudio.Shell.Embeddable" Version="15.6.27002-vsuvscore" />
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.11.0">
<Version>11.0.61030</Version>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.12.0">
<Version>12.0.30110</Version>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.15.3.DesignTime">
<Version>15.0.26606</Version>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.8.0">
<Version>8.0.50727</Version>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.9.0">
<Version>9.0.30729</Version>
</PackageReference>
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="15.1.192" />
<PackageReference Include="System.ValueTuple">
<Version>4.4.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Content Include="Resources\SecretManagerTestCommand.png" />
<Content Include="Resources\SecretManagerTestPackage.ico" />
<VSCTCompile Include="SecretManagerTestPackage.vsct">
<ResourceName>Menus.ctmenu</ResourceName>
</VSCTCompile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="VSPackage.resx">
<MergeWithCTO>true</MergeWithCTO>
<ManifestResourceName>VSPackage</ManifestResourceName>
</EmbeddedResource>
</ItemGroup>
<Target Name="GetBuildVersion" Outputs="$(VsixVersion)" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -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.ComponentModel;
using System.Runtime.CompilerServices;
namespace Microsoft.VisualStudio.SecretManager.TestExtension
{
public abstract class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@ -0,0 +1,20 @@
// 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 Microsoft.VisualStudio.ProjectSystem;
namespace Microsoft.VisualStudio.SecretManager.TestExtension
{
public class ProjectViewModel : NotifyPropertyChanged
{
public ProjectViewModel(UnconfiguredProject project)
{
Project = project;
}
internal UnconfiguredProject Project { get; }
public string ProjectName => Path.GetFileNameWithoutExtension(Project.FullPath);
}
}

View File

@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Microsoft.VisualStudio.SecretManager.TestExtension")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Microsoft.VisualStudio.SecretManager.TestExtension")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,44 @@
// 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.Windows.Input;
namespace Microsoft.VisualStudio.SecretManager.TestExtension
{
public class RelayCommand<T> : ICommand
{
readonly Action<T> _execute = null;
readonly Predicate<T> _canExecute = null;
public RelayCommand(Action<T> execute)
: this(execute, null)
{
}
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

View File

@ -0,0 +1,100 @@
// 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.ComponentModel.Design;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.SecretManager.TestExtension
{
internal sealed class SecretManagerTestCommand
{
/// <summary>
/// Command ID.
/// </summary>
public const int CommandId = 0x0100;
/// <summary>
/// Command menu group (command set GUID).
/// </summary>
public static readonly Guid CommandSet = new Guid("e415a3f4-f2a8-4834-b7f7-f89844b2505c");
/// <summary>
/// VS Package that provides this command, not null.
/// </summary>
private readonly Package package;
/// <summary>
/// Initializes a new instance of the <see cref="SecretManagerTestCommand"/> class.
/// Adds our command handlers for menu (commands must exist in the command table file)
/// </summary>
/// <param name="package">Owner package, not null.</param>
private SecretManagerTestCommand(Package package)
{
if (package == null)
{
throw new ArgumentNullException("package");
}
this.package = package;
OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService != null)
{
var menuCommandID = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(this.ShowToolWindow, menuCommandID);
commandService.AddCommand(menuItem);
}
}
/// <summary>
/// Gets the instance of the command.
/// </summary>
public static SecretManagerTestCommand Instance
{
get;
private set;
}
/// <summary>
/// Gets the service provider from the owner package.
/// </summary>
private IServiceProvider ServiceProvider
{
get
{
return this.package;
}
}
/// <summary>
/// Initializes the singleton instance of the command.
/// </summary>
/// <param name="package">Owner package, not null.</param>
public static void Initialize(Package package)
{
Instance = new SecretManagerTestCommand(package);
}
/// <summary>
/// Shows the tool window when the menu item is clicked.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
private void ShowToolWindow(object sender, EventArgs e)
{
// Get the instance number 0 of this tool window. This window is single instance so this instance
// is actually the only one.
// The last flag is set to true so that if the tool window does not exists it will be created.
ToolWindowPane window = this.package.FindToolWindow(typeof(SecretManagerTestWindow), 0, true);
if ((null == window) || (null == window.Frame))
{
throw new NotSupportedException("Cannot create tool window");
}
IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame;
Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show());
}
}
}

View File

@ -0,0 +1,87 @@
<UserControl
x:Class="Microsoft.VisualStudio.SecretManager.TestExtension.SecretManagerTestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Background="{DynamicResource VsBrush.Window}"
Foreground="{DynamicResource VsBrush.WindowText}"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="Auto" />
<ColumnDefinition
Width="*" />
<ColumnDefinition
Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition
Height="Auto" />
<RowDefinition
Height="Auto" />
<RowDefinition
Height="*" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<StackPanel
Grid.ColumnSpan="3"
Grid.Row="0"
Visibility="{Binding ErrorVisibility}">
<Label Content="{Binding Error}" HorizontalContentAlignment="Center" Foreground="Red"/>
</StackPanel>
<Label
Grid.Column="0"
Grid.Row="1"
Content="Select Project"
Margin="5"
Width="Auto" />
<ComboBox
Grid.Column="1"
Grid.Row="1"
ItemsSource="{Binding Projects}"
SelectedItem="{Binding SelectedProject}"
DisplayMemberPath="ProjectName"
Margin="5"
HorizontalAlignment="Stretch" />
<Button
Grid.Column="2"
Grid.Row="1"
Margin="5"
Content="Refresh"
Command="{Binding RefreshCommand}" />
<ListView
Grid.ColumnSpan="3"
Grid.Row="2"
ItemsSource="{Binding Secrets}"
IsEnabled="{Binding IsLoaded}">
<ListView.View>
<GridView>
<GridViewColumn
Header="Key"
DisplayMemberBinding="{Binding Key}" />
<GridViewColumn
Header="Value"
DisplayMemberBinding="{Binding Value}" />
</GridView>
</ListView.View>
</ListView>
<StackPanel
Margin="4"
Orientation="Horizontal"
Grid.Row="3">
<Button Padding="10,3" Command="{Binding AddCommand}">Add random value</Button>
<Button Padding="10,3" Margin="5,0,0,0" Command="{Binding SaveCommand}">Save</Button>
</StackPanel>
</Grid>
</UserControl>

View File

@ -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.
namespace Microsoft.VisualStudio.SecretManager.TestExtension
{
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Controls;
/// <summary>
/// Interaction logic for SecretManagerTestControl.
/// </summary>
public partial class SecretManagerTestControl : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="SecretManagerTestControl"/> class.
/// </summary>
public SecretManagerTestControl()
{
this.InitializeComponent();
}
/// <summary>
/// Handles click on the button by displaying a message box.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
[SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Justification = "Sample code")]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Default event handler naming pattern")]
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(
string.Format(System.Globalization.CultureInfo.CurrentUICulture, "Invoked '{0}'", this.ToString()),
"SecretManagerTest");
}
}
}

View File

@ -0,0 +1,27 @@
// 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.CodeAnalysis;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
namespace Microsoft.VisualStudio.SecretManager.TestExtension
{
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About
[ProvideMenuResource("Menus.ctmenu", 1)]
[ProvideToolWindow(typeof(SecretManagerTestWindow))]
[Guid(SecretManagerTestPackage.PackageGuidString)]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
public sealed class SecretManagerTestPackage : Package
{
public const string PackageGuidString = "7b771e3e-f599-4fde-95a9-e35019e705f7";
protected override void Initialize()
{
SecretManagerTestCommand.Initialize(this);
base.Initialize();
}
}
}

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This is the file that defines the actual layout and type of the commands.
It is divided in different sections (e.g. command definition, command
placement, ...), with each defining a specific set of properties.
See the comment before each section for more details about how to
use it. -->
<!-- The VSCT compiler (the tool that translates this file into the binary
format that VisualStudio will consume) has the ability to run a preprocessor
on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
it is possible to define includes and macros with the same syntax used
in C++ files. Using this ability of the compiler here, we include some files
defining some of the constants that we will use inside the file. -->
<!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
<Extern href="stdidcmd.h"/>
<!--This header contains the command ids for the menus provided by the shell. -->
<Extern href="vsshlids.h"/>
<!--The Commands section is where commands, menus, and menu groups are defined.
This section uses a Guid to identify the package that provides the command defined inside it. -->
<Commands package="guidSecretManagerTestPackage">
<!-- Inside this section we have different sub-sections: one for the menus, another
for the menu groups, one for the buttons (the actual commands), one for the combos
and the last one for the bitmaps used. Each element is identified by a command id that
is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
called "command set" and is used to group different command inside a logically related
group; your package should define its own command set in order to avoid collisions
with command ids defined by other packages. -->
<!--Buttons section. -->
<!--This section defines the elements the user can interact with, like a menu command or a button
or combo box in a toolbar. -->
<Buttons>
<!--To define a menu group you have to specify its ID, the parent menu and its display priority.
The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
the CommandFlag node.
You can add more than one CommandFlag node e.g.:
<CommandFlag>DefaultInvisible</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
If you do not want an image next to your command, remove the Icon node /> -->
<Button guid="guidSecretManagerTestPackageCmdSet" id="SecretManagerTestCommandId" priority="0x0100" type="Button">
<Parent guid="guidSHLMainMenu" id="IDG_VS_WNDO_OTRWNDWS1"/>
<Icon guid="guidImages" id="bmpPic1" />
<Strings>
<ButtonText>SecretManager Test</ButtonText>
</Strings>
</Button>
</Buttons>
<!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
<Bitmaps>
<!-- The bitmap id is defined in a way that is a little bit different from the others:
the declaration starts with a guid for the bitmap strip, then there is the resource id of the
bitmap strip containing the bitmaps and then there are the numeric ids of the elements used
inside a button definition. An important aspect of this declaration is that the element id
must be the actual index (1-based) of the bitmap inside the bitmap strip. -->
<Bitmap guid="guidImages" href="Resources\SecretManagerTestCommand.png" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows, bmpPicStrikethrough"/>
</Bitmaps>
</Commands>
<Symbols>
<!-- This is the package guid. -->
<GuidSymbol name="guidSecretManagerTestPackage" value="{7b771e3e-f599-4fde-95a9-e35019e705f7}" />
<!-- This is the guid used to group the menu commands together -->
<GuidSymbol name="guidSecretManagerTestPackageCmdSet" value="{e415a3f4-f2a8-4834-b7f7-f89844b2505c}">
<IDSymbol name="SecretManagerTestCommandId" value="0x0100" />
</GuidSymbol>
<GuidSymbol name="guidImages" value="{ab6e0ec7-5f1f-4134-a2b5-28e528fac523}" >
<IDSymbol name="bmpPic1" value="1" />
<IDSymbol name="bmpPic2" value="2" />
<IDSymbol name="bmpPicSearch" value="3" />
<IDSymbol name="bmpPicX" value="4" />
<IDSymbol name="bmpPicArrows" value="5" />
<IDSymbol name="bmpPicStrikethrough" value="6" />
</GuidSymbol>
</Symbols>
</CommandTable>

View File

@ -0,0 +1,31 @@
// 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.Runtime.InteropServices;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.Shell;
namespace Microsoft.VisualStudio.SecretManager.TestExtension
{
[Guid("6afffd63-17b6-4ef2-b515-fee22d767631")]
public class SecretManagerTestWindow : ToolWindowPane
{
public SecretManagerTestWindow()
: base(null)
{
this.Caption = "SecretManager Test Window";
this.Content = new SecretManagerTestControl();
}
protected override void Initialize()
{
base.Initialize();
var component = (IComponentModel)GetService(typeof(SComponentModel));
var projectService = component.GetService<IProjectServiceAccessor>().GetProjectService();
((SecretManagerTestControl)Content).DataContext = new SecretManagerViewModel(projectService);
}
}
}

View File

@ -0,0 +1,214 @@
// 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.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Threading;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
using Task = System.Threading.Tasks.Task;
namespace Microsoft.VisualStudio.SecretManager.TestExtension
{
public class SecretManagerViewModel : NotifyPropertyChanged
{
private readonly IProjectService _projectService;
private readonly Random _rand;
private string _error;
private bool _isLoaded;
private ProjectViewModel _selectedProject;
public SecretManagerViewModel(IProjectService projectService)
{
_projectService = projectService;
RefreshCommand = new RelayCommand<object>(Refresh, RefreshIsEnabled);
AddCommand = new RelayCommand<object>(Add, IsProjectLoaded);
SaveCommand = new RelayCommand<object>(Save, IsProjectLoaded);
Refresh(null);
_rand = new Random();
}
public RelayCommand<object> RefreshCommand { get; }
public RelayCommand<object> AddCommand { get; }
public RelayCommand<object> SaveCommand { get; }
public ObservableCollection<ProjectViewModel> Projects { get; } = new ObservableCollection<ProjectViewModel>();
public ProjectViewModel SelectedProject
{
get => _selectedProject;
set
{
if (value == _selectedProject)
{
return;
}
_selectedProject = value;
OnSelectedProjectChanged();
OnPropertyChanged();
}
}
public bool IsLoaded
{
get => _isLoaded;
set
{
if (value == _isLoaded)
{
return;
}
_isLoaded = value;
OnPropertyChanged();
}
}
public string Error
{
get => _error;
set
{
if (value == _error)
{
return;
}
_error = value;
OnPropertyChanged();
OnPropertyChanged(nameof(ErrorVisibility));
}
}
public Visibility ErrorVisibility => Error == null ? Visibility.Collapsed : Visibility.Visible;
public ObservableCollection<KeyValuePair<string, string>> Secrets { get; } = new ObservableCollection<KeyValuePair<string, string>>();
private bool RefreshIsEnabled(object obj) => IsLoaded || SelectedProject == null;
private void Refresh(object obj)
{
Projects.Clear();
foreach (var project in _projectService.LoadedUnconfiguredProjects)
{
Projects.Add(new ProjectViewModel(project));
}
}
private bool IsProjectLoaded(object obj) => IsLoaded && SelectedProject != null;
private void Add(object obj)
{
Secrets.Add(new KeyValuePair<string, string>("NewKey" + _rand.Next(10_000), "My new totally random and secret test value"));
}
private async void Save(object obj)
{
Exception exception;
try
{
IOleServiceProvider oleServices;
var project = (IVsProject)_selectedProject.Project.Services.HostObject;
Marshal.ThrowExceptionForHR(project.GetItemContext((uint)VSConstants.VSITEMID.Root, out oleServices));
var services = new ServiceProvider(oleServices);
var projectSecrets = (IVsProjectSecrets)services.GetService(typeof(SVsProjectLocalSecrets));
await TaskScheduler.Default;
if (projectSecrets == null)
{
exception = null;
}
else
{
foreach (var secret in Secrets)
{
await projectSecrets.SetSecretAsync(secret.Key, secret.Value).ConfigureAwait(false);
}
exception = null;
}
}
catch (Exception ex)
{
exception = ex;
}
if (exception != null)
{
Error = exception.ToString();
}
}
private async void OnSelectedProjectChanged()
{
Secrets.Clear();
IsLoaded = false;
if (_selectedProject == null)
{
return;
}
KeyValuePair<string, string>[] results;
Exception exception;
try
{
IOleServiceProvider oleServices;
var project = (IVsProject)_selectedProject.Project.Services.HostObject;
Marshal.ThrowExceptionForHR(project.GetItemContext((uint)VSConstants.VSITEMID.Root, out oleServices));
var services = new ServiceProvider(oleServices);
var projectSecrets = (IVsProjectSecrets)services.GetService(typeof(SVsProjectLocalSecrets));
await TaskScheduler.Default;
if (projectSecrets == null)
{
results = null;
exception = null;
}
else
{
var secrets = await projectSecrets.GetSecretsAsync().ConfigureAwait(false);
results = secrets.ToArray();
exception = null;
}
}
catch (Exception ex)
{
results = null;
exception = ex;
}
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
if (exception != null)
{
Error = exception.ToString();
}
else if (results != null)
{
for (var i = 0; i < results.Length; i++)
{
Secrets.Add(results[i]);
}
}
IsLoaded = true;
}
}
}

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
VS SDK Notes: This resx file contains the resources that will be consumed from your package by Visual Studio.
For example, Visual Studio will attempt to load resource '400' from this resource stream when it needs to
load your package's icon. Because Visual Studio will always look in the VSPackage.resources stream first for
resources it needs, you should put additional resources that Visual Studio will load directly into this resx
file.
Resources that you would like to access directly from your package in a strong-typed fashion should be stored
in Resources.resx or another resx file.
-->
<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>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="110" xml:space="preserve">
<value>SecretManagerTest Extension</value>
</data>
<data name="112" xml:space="preserve">
<value>SecretManagerTest Visual Studio Extension Detailed Info</value>
</data>
<data name="400" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\SecretManagerTestPackage.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.2.0" newVersion="1.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.VisualStudio.Validation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.3.0.0" newVersion="15.3.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Composition.AttributedModel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.0.32.0" newVersion="1.0.32.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Dataflow" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.6.2.0" newVersion="4.6.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Composition.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.0.32.0" newVersion="1.0.32.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="Microsoft.VisualStudio.SecretManager.TestExtension" Version="|%CurrentProject%;GetBuildVersion|" Language="en-US" Publisher="Microsoft" />
<DisplayName>Microsoft.VisualStudio.SecretManager.TestExtension</DisplayName>
<Description xml:space="preserve">A test extension for Microsoft.VisualStudio.TestExtension</Description>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[15.0]" />
</Installation>
<Dependencies>
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" />
<Dependency Id="Microsoft.VisualStudio.MPF.15.0" DisplayName="Visual Studio MPF 15.0" d:Source="Installed" Version="[15.0]" />
</Dependencies>
<Prerequisites>
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[15.0,16.0)" DisplayName="Visual Studio core editor" />
</Prerequisites>
<Assets>
<Asset Type="Microsoft.VisualStudio.VsPackage" d:Source="Project" d:ProjectName="%CurrentProject%" Path="|%CurrentProject%;PkgdefProjectOutputGroup|" />
</Assets>
</PackageManifest>

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ImportDirectoryBuildProps>False</ImportDirectoryBuildProps>
<ImportDirectoryBuildTargets>False</ImportDirectoryBuildTargets>
<GenerateUserSecretsAttribute>False</GenerateUserSecretsAttribute>
<UseCodebase>True</UseCodebase>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\build\Key.snk</AssemblyOriginatorKeyFile>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<Import Project="..\..\version.props" />
<Import Project="..\..\build\dependencies.props" />
<Import Project="..\..\build\sources.props" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<ProjectGuid>{5E117F2E-7152-447F-BF47-59F759EEF3A7}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.VisualStudio.SecretManager</RootNamespace>
<AssemblyName>Microsoft.VisualStudio.SecretManager</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<GeneratePkgDefFile>true</GeneratePkgDefFile>
<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
<IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>true</IncludeDebugSymbolsInLocalVSIXDeployment>
<CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
<StartAction>Program</StartAction>
<StartProgram Condition="'$(DevEnvDir)' != ''">$(DevEnvDir)devenv.exe</StartProgram>
<StartArguments>/rootsuffix Exp</StartArguments>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;EXTENSION_DEVELOPER_MODE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="ProjectLocalSecretsManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources.Designer.cs" />
<Compile Include="SecretManagerFactory.cs" />
<Compile Include="SecretStore.cs" />
<Compile Include="Sources\ConfigurationPath.cs" />
<Compile Include="Sources\JsonConfigurationFileParser.cs" />
<Compile Include="Sources\PathHelper.cs" />
</ItemGroup>
<ItemGroup>
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.ComponentModelHost" Version="15.0.26606" />
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.Analyzers" Version="15.3.224" />
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.SDK.Tools" Version="15.3.224" />
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.SDK" Version="15.3.224" />
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem" Version="15.3.224" />
<PackageReference Include="Microsoft.VisualStudio.Shell.15.0" Version="15.0.26606" />
<PackageReference Include="Microsoft.VisualStudio.Shell.Embeddable" Version="15.6.27002-vsuvscore" />
<PackageReference Include="Microsoft.VisualStudio.Text.Data" Version="15.0.26606" />
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="15.1.192" />
</ItemGroup>
<!--
Begin workaround for https://github.com/dotnet/project-system/issues/2129
VS does not support using MSBuild variables for PackageReference items
-->
<ItemGroup Condition="'$(PackageReferencePropsPath)' == ''">
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup>
<Import Project="$(PackageReferencePropsPath)" Condition="'$(PackageReferencePropsPath)' != ''" />
<!-- End workaround -->
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Target Name="GetBuildVersion" Outputs="$(VsixVersion)" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,148 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.ProjectSystem.Properties;
using Microsoft.VisualStudio.Threading;
using Task = System.Threading.Tasks.Task;
namespace Microsoft.VisualStudio.SecretManager
{
/// <summary>
/// Provides an thread-safe access the secrets.json file based on the UserSecretsId property in a configured project.
/// </summary>
internal class ProjectLocalSecretsManager : Shell.IVsProjectSecrets, Shell.SVsProjectLocalSecrets
{
private const string UserSecretsPropertyName = "UserSecretsId";
private readonly AsyncSemaphore _semaphore;
private readonly IProjectPropertiesProvider _propertiesProvider;
private readonly Lazy<IServiceProvider> _services;
public ProjectLocalSecretsManager(IProjectPropertiesProvider propertiesProvider, Lazy<IServiceProvider> serviceProvider)
{
_propertiesProvider = propertiesProvider ?? throw new ArgumentNullException(nameof(propertiesProvider));
_services = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
_semaphore = new AsyncSemaphore(1);
}
public string SanitizeName(string name) => name;
public IReadOnlyCollection<char> GetInvalidCharactersFrom(string name) => Array.Empty<char>();
public async Task AddSecretAsync(string name, string value, CancellationToken cancellationToken = default)
{
EnsureKeyNameIsValid(name);
await TaskScheduler.Default;
using (await _semaphore.EnterAsync(cancellationToken))
using (var store = await GetOrCreateStoreAsync(cancellationToken))
{
if (store.ContainsKey(name))
{
throw new ArgumentException(Resources.Error_SecretAlreadyExists, nameof(name));
}
store.Set(name, value);
await store.SaveAsync(cancellationToken);
}
}
public async Task SetSecretAsync(string name, string value, CancellationToken cancellationToken = default)
{
EnsureKeyNameIsValid(name);
await TaskScheduler.Default;
using (await _semaphore.EnterAsync(cancellationToken))
using (var store = await GetOrCreateStoreAsync(cancellationToken))
{
store.Set(name, value);
await store.SaveAsync(cancellationToken);
}
}
public async Task<string> GetSecretAsync(string name, CancellationToken cancellationToken = default)
{
EnsureKeyNameIsValid(name);
await TaskScheduler.Default;
using (await _semaphore.EnterAsync(cancellationToken))
using (var store = await GetOrCreateStoreAsync(cancellationToken))
{
return store.Get(name);
}
}
public async Task<IReadOnlyCollection<string>> GetSecretNamesAsync(CancellationToken cancellationToken = default)
{
await TaskScheduler.Default;
using (await _semaphore.EnterAsync(cancellationToken))
using (var store = await GetOrCreateStoreAsync(cancellationToken))
{
return store.ReadOnlyKeys;
}
}
public async Task<IReadOnlyDictionary<string, string>> GetSecretsAsync(CancellationToken cancellationToken = default)
{
await TaskScheduler.Default;
using (await _semaphore.EnterAsync(cancellationToken))
using (var store = await GetOrCreateStoreAsync(cancellationToken))
{
return store.Values;
}
}
public async Task<bool> RemoveSecretAsync(string name, CancellationToken cancellationToken = default)
{
EnsureKeyNameIsValid(name);
await TaskScheduler.Default;
using (await _semaphore.EnterAsync(cancellationToken))
using (var store = await GetOrCreateStoreAsync(cancellationToken))
{
if (store.Remove(name))
{
await store.SaveAsync(cancellationToken);
return true;
}
return false;
}
}
private void EnsureKeyNameIsValid(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (name.Length == 0)
{
throw new ArgumentException(nameof(name));
}
}
private async Task<SecretStore> GetOrCreateStoreAsync(CancellationToken cancellationToken)
{
var userSecretsId = await _propertiesProvider.GetCommonProperties().GetEvaluatedPropertyValueAsync(UserSecretsPropertyName);
if (string.IsNullOrEmpty(userSecretsId))
{
userSecretsId = Guid.NewGuid().ToString();
await _propertiesProvider.GetCommonProperties().SetPropertyValueAsync(UserSecretsPropertyName, userSecretsId);
}
var store = new SecretStore(userSecretsId);
await store.LoadAsync(cancellationToken);
return store;
}
}
}

View File

@ -0,0 +1,16 @@
// 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 Microsoft.VisualStudio.Shell;
// required for VS to generate the pkgdef
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: ProvideCodeBase(CodeBase = @"$PackageFolder$\Microsoft.VisualStudio.SecretManager.dll")]
[assembly: ProvideBindingRedirection(
AssemblyName = "Microsoft.VisualStudio.SecretManager",
GenerateCodeBase = true,
PublicKeyToken = "adb9793829ddae60",
OldVersionLowerBound = "0.0.0.0",
OldVersionUpperBound = "1.0.0.0",
NewVersion = "1.0.0.0")]

View File

@ -0,0 +1,140 @@
// <auto-generated />
namespace Microsoft.VisualStudio.SecretManager
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.VisualStudio.SecretManager.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// A secret with this name already exists.
/// </summary>
internal static string Error_SecretAlreadyExists
{
get => GetString("Error_SecretAlreadyExists");
}
/// <summary>
/// Value cannot be null or an empty string.
/// </summary>
internal static string Common_StringNullOrEmpty
{
get => GetString("Common_StringNullOrEmpty");
}
/// <summary>
/// Value cannot be null or an empty string.
/// </summary>
internal static string FormatCommon_StringNullOrEmpty()
=> GetString("Common_StringNullOrEmpty");
/// <summary>
/// Invalid character '{0}' found in the user secrets ID at index '{1}'.
/// </summary>
internal static string Error_Invalid_Character_In_UserSecrets_Id
{
get => GetString("Error_Invalid_Character_In_UserSecrets_Id");
}
/// <summary>
/// Invalid character '{0}' found in the user secrets ID at index '{1}'.
/// </summary>
internal static string FormatError_Invalid_Character_In_UserSecrets_Id(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("Error_Invalid_Character_In_UserSecrets_Id"), p0, p1);
/// <summary>
/// Could not find 'UserSecretsIdAttribute' on assembly '{0}'.
/// Check that the project for '{0}' has set the 'UserSecretsId' build property.
/// If the 'UserSecretsId' property is already set then add a reference to the Microsoft.Extensions.Configuration.UserSecrets package.
/// </summary>
internal static string Error_Missing_UserSecretsIdAttribute
{
get => GetString("Error_Missing_UserSecretsIdAttribute");
}
/// <summary>
/// Could not find 'UserSecretsIdAttribute' on assembly '{0}'.
/// Check that the project for '{0}' has set the 'UserSecretsId' build property.
/// If the 'UserSecretsId' property is already set then add a reference to the Microsoft.Extensions.Configuration.UserSecrets package.
/// </summary>
internal static string FormatError_Missing_UserSecretsIdAttribute(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("Error_Missing_UserSecretsIdAttribute"), p0);
/// <summary>
/// File path must be a non-empty string.
/// </summary>
internal static string Error_InvalidFilePath
{
get => GetString("Error_InvalidFilePath");
}
/// <summary>
/// File path must be a non-empty string.
/// </summary>
internal static string FormatError_InvalidFilePath()
=> GetString("Error_InvalidFilePath");
/// <summary>
/// Could not parse the JSON file. Error on line number '{0}': '{1}'.
/// </summary>
internal static string Error_JSONParseError
{
get => GetString("Error_JSONParseError");
}
/// <summary>
/// Could not parse the JSON file. Error on line number '{0}': '{1}'.
/// </summary>
internal static string FormatError_JSONParseError(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("Error_JSONParseError"), p0, p1);
/// <summary>
/// A duplicate key '{0}' was found.
/// </summary>
internal static string Error_KeyIsDuplicated
{
get => GetString("Error_KeyIsDuplicated");
}
/// <summary>
/// A duplicate key '{0}' was found.
/// </summary>
internal static string FormatError_KeyIsDuplicated(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("Error_KeyIsDuplicated"), p0);
/// <summary>
/// Unsupported JSON token '{0}' was found. Path '{1}', line {2} position {3}.
/// </summary>
internal static string Error_UnsupportedJSONToken
{
get => GetString("Error_UnsupportedJSONToken");
}
/// <summary>
/// Unsupported JSON token '{0}' was found. Path '{1}', line {2} position {3}.
/// </summary>
internal static string FormatError_UnsupportedJSONToken(object p0, object p1, object p2, object p3)
=> string.Format(CultureInfo.CurrentCulture, GetString("Error_UnsupportedJSONToken"), p0, p1, p2, p3);
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;
}
}
}

View File

@ -0,0 +1,146 @@
<?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_InvalidFilePath" xml:space="preserve">
<value>File path must be a non-empty string.</value>
</data>
<data name="Error_JSONParseError" xml:space="preserve">
<value>Could not parse the JSON file. Error on line number '{0}': '{1}'.</value>
</data>
<data name="Error_KeyIsDuplicated" xml:space="preserve">
<value>A duplicate key '{0}' was found.</value>
</data>
<data name="Error_UnsupportedJSONToken" xml:space="preserve">
<value>Unsupported JSON token '{0}' was found. Path '{1}', line {2} position {3}.</value>
</data>
<data name="Common_StringNullOrEmpty" xml:space="preserve">
<value>Value cannot be null or an empty string.</value>
</data>
<data name="Error_Invalid_Character_In_UserSecrets_Id" xml:space="preserve">
<value>Invalid character '{0}' found in the user secrets ID at index '{1}'.</value>
</data>
<data name="Error_Missing_UserSecretsIdAttribute" xml:space="preserve">
<value>Could not find 'UserSecretsIdAttribute' on assembly '{0}'.
Check that the project for '{0}' has set the 'UserSecretsId' build property.
If the 'UserSecretsId' property is already set then add a reference to the Microsoft.Extensions.Configuration.UserSecrets package.</value>
</data>
<data name="Error_SecretAlreadyExists" xml:space="preserve">
<value>A secret with this name already exists.</value>
</data>
</root>

View File

@ -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 System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.Shell;
namespace Microsoft.VisualStudio.SecretManager
{
internal class SecretManagerFactory
{
// This is capability is set in Microsoft.Extensions.Configuration.UserSecrets
private const string CapabilityName = "LocalUserSecrets";
private readonly Lazy<ProjectLocalSecretsManager> _secretManager;
private readonly UnconfiguredProject _project;
[ImportingConstructor]
public SecretManagerFactory(UnconfiguredProject project, SVsServiceProvider vsServiceProvider)
{
_project = project;
var serviceProvider = new Lazy<IServiceProvider>(() => vsServiceProvider);
_secretManager = new Lazy<ProjectLocalSecretsManager>(() =>
{
var propertiesProvider = _project.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject.Services.ProjectPropertiesProvider;
return new ProjectLocalSecretsManager(propertiesProvider, serviceProvider);
});
}
[ExportVsProfferedProjectService(typeof(SVsProjectLocalSecrets))]
[AppliesTo(CapabilityName)]
public SVsProjectLocalSecrets ProjectLocalSecretsManager => _secretManager.Value;
}
}

View File

@ -0,0 +1,169 @@
// 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 System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.VisualStudio.Threading;
using Newtonsoft.Json.Linq;
using Task = System.Threading.Tasks.Task;
namespace Microsoft.VisualStudio.SecretManager
{
/// <summary>
/// Provides read and write access to the secrets.json file for local user secrets.
/// This is not thread-safe.
/// This object is meant to have a short lifetime.
/// When calling <see cref="SaveAsync(CancellationToken)"/>, this will overwrite the secrets.json file. It does not check for concurrency issues if another process has edited this file.
/// </summary>
internal class SecretStore : IDisposable
{
private Dictionary<string, string> _secrets;
private string _fileDir;
private string _filePath;
private bool _isDirty;
private volatile bool _disposed;
public SecretStore(string userSecretsId)
{
_filePath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId);
_fileDir = Path.GetDirectoryName(_filePath);
}
public IReadOnlyCollection<string> ReadOnlyKeys
{
get
{
EnsureNotDisposed();
return _secrets.Keys;
}
}
public IReadOnlyDictionary<string, string> Values
{
get
{
EnsureNotDisposed();
return _secrets;
}
}
public bool ContainsKey(string key)
{
EnsureNotDisposed();
return _secrets.ContainsKey(key);
}
public string Get(string name)
{
EnsureNotDisposed();
return _secrets[name];
}
public void Set(string key, string value)
{
EnsureNotDisposed();
_isDirty = true;
_secrets[key] = value;
}
public bool Remove(string key)
{
EnsureNotDisposed();
_isDirty = true;
return _secrets.Remove(key);
}
public async Task LoadAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await TaskScheduler.Default;
EnsureNotDisposed();
string text = null;
if (File.Exists(_filePath))
{
text = File.ReadAllText(_filePath);
}
_secrets = DeserializeJson(text);
}
public async Task SaveAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await TaskScheduler.Default;
EnsureNotDisposed();
if (!_isDirty)
{
return;
}
Directory.CreateDirectory(_fileDir);
File.WriteAllText(_filePath, Stringify(_secrets), Encoding.UTF8);
_isDirty = false;
}
private void EnsureNotDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(SecretStore));
}
}
private static string Stringify(Dictionary<string, string> secrets)
{
var contents = new JObject();
if (secrets != null)
{
foreach (var secret in secrets)
{
contents[secret.Key] = secret.Value;
}
}
return contents.ToString();
}
private static Dictionary<string, string> DeserializeJson(string text)
{
if (string.IsNullOrEmpty(text))
{
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
using (var stream = new MemoryStream())
{
var bytes = Encoding.UTF8.GetBytes(text);
stream.Write(bytes, 0, bytes.Length);
stream.Position = 0;
// might throw FormatException if JSON is malformed.
var data = JsonConfigurationFileParser.Parse(stream);
return new Dictionary<string, string>(data, StringComparer.OrdinalIgnoreCase);
}
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
}
}
}

View File

@ -0,0 +1,79 @@
// 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;
namespace Microsoft.Extensions.Configuration
{
/// <summary>
/// Utility methods and constants for manipulating Configuration paths
/// </summary>
internal static class ConfigurationPath
{
/// <summary>
/// The delimiter ":" used to separate individual keys in a path.
/// </summary>
public static readonly string KeyDelimiter = ":";
/// <summary>
/// Combines path segments into one path.
/// </summary>
/// <param name="pathSegments">The path segments to combine.</param>
/// <returns>The combined path.</returns>
public static string Combine(params string[] pathSegments)
{
if (pathSegments == null)
{
throw new ArgumentNullException(nameof(pathSegments));
}
return string.Join(KeyDelimiter, pathSegments);
}
/// <summary>
/// Combines path segments into one path.
/// </summary>
/// <param name="pathSegments">The path segments to combine.</param>
/// <returns>The combined path.</returns>
public static string Combine(IEnumerable<string> pathSegments)
{
if (pathSegments == null)
{
throw new ArgumentNullException(nameof(pathSegments));
}
return string.Join(KeyDelimiter, pathSegments);
}
/// <summary>
/// Extracts the last path segment from the path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>The last path segment of the path.</returns>
public static string GetSectionKey(string path)
{
if (string.IsNullOrEmpty(path))
{
return path;
}
var lastDelimiterIndex = path.LastIndexOf(KeyDelimiter, StringComparison.OrdinalIgnoreCase);
return lastDelimiterIndex == -1 ? path : path.Substring(lastDelimiterIndex + 1);
}
/// <summary>
/// Extracts the path corresponding to the parent node for a given path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>The original path minus the last individual segment found in it. Null if the original path corresponds to a top level node.</returns>
public static string GetParentPath(string path)
{
if (string.IsNullOrEmpty(path))
{
return null;
}
var lastDelimiterIndex = path.LastIndexOf(KeyDelimiter, StringComparison.OrdinalIgnoreCase);
return lastDelimiterIndex == -1 ? null : path.Substring(0, lastDelimiterIndex);
}
}
}

View File

@ -0,0 +1,120 @@
// 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.Globalization;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.SecretManager;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Extensions.Configuration.Json
{
internal class JsonConfigurationFileParser
{
private JsonConfigurationFileParser() { }
private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _context = new Stack<string>();
private string _currentPath;
private JsonTextReader _reader;
public static IDictionary<string, string> Parse(Stream input)
=> new JsonConfigurationFileParser().ParseStream(input);
private IDictionary<string, string> ParseStream(Stream input)
{
_data.Clear();
_reader = new JsonTextReader(new StreamReader(input));
_reader.DateParseHandling = DateParseHandling.None;
var jsonConfig = JObject.Load(_reader);
VisitJObject(jsonConfig);
return _data;
}
private void VisitJObject(JObject jObject)
{
foreach (var property in jObject.Properties())
{
EnterContext(property.Name);
VisitProperty(property);
ExitContext();
}
}
private void VisitProperty(JProperty property)
{
VisitToken(property.Value);
}
private void VisitToken(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
VisitJObject(token.Value<JObject>());
break;
case JTokenType.Array:
VisitArray(token.Value<JArray>());
break;
case JTokenType.Integer:
case JTokenType.Float:
case JTokenType.String:
case JTokenType.Boolean:
case JTokenType.Bytes:
case JTokenType.Raw:
case JTokenType.Null:
VisitPrimitive(token.Value<JValue>());
break;
default:
throw new FormatException(Resources.FormatError_UnsupportedJSONToken(
_reader.TokenType,
_reader.Path,
_reader.LineNumber,
_reader.LinePosition));
}
}
private void VisitArray(JArray array)
{
for (int index = 0; index < array.Count; index++)
{
EnterContext(index.ToString());
VisitToken(array[index]);
ExitContext();
}
}
private void VisitPrimitive(JValue data)
{
var key = _currentPath;
if (_data.ContainsKey(key))
{
throw new FormatException(Resources.FormatError_KeyIsDuplicated(key));
}
_data[key] = data.ToString(CultureInfo.InvariantCulture);
}
private void EnterContext(string context)
{
_context.Push(context);
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
private void ExitContext()
{
_context.Pop();
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
}
}

View File

@ -0,0 +1,57 @@
// 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.VisualStudio.SecretManager;
namespace Microsoft.Extensions.Configuration.UserSecrets
{
/// <summary>
/// Provides paths for user secrets configuration files.
/// </summary>
internal class PathHelper
{
internal const string SecretsFileName = "secrets.json";
/// <summary>
/// <para>
/// Returns the path to the JSON file that stores user secrets.
/// </para>
/// <para>
/// This uses the current user profile to locate the secrets file on disk in a location outside of source control.
/// </para>
/// </summary>
/// <param name="userSecretsId">The user secret ID.</param>
/// <returns>The full path to the secret file.</returns>
public static string GetSecretsPathFromSecretsId(string userSecretsId)
{
if (string.IsNullOrEmpty(userSecretsId))
{
throw new ArgumentException(Resources.Common_StringNullOrEmpty, nameof(userSecretsId));
}
var badCharIndex = userSecretsId.IndexOfAny(Path.GetInvalidFileNameChars());
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, SecretsFileName);
}
else
{
return Path.Combine(root, ".microsoft", "usersecrets", userSecretsId, SecretsFileName);
}
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="Microsoft.VisualStudio.SecretManager.11eea7f6-8d34-4759-b618-b40f454266d4" Version="|%CurrentProject%;GetBuildVersion|" Language="en-US" Publisher="Microsoft" />
<DisplayName>Microsoft.VisualStudio.SecretManager</DisplayName>
<Description xml:space="preserve">Enables IVsProjectSecrets for ASP.NET Core projects that use Microsoft.Extensions.Configuration.UserSecrets.</Description>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[15.0.26228.04,)" />
</Installation>
<Dependencies>
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.6,)" />
<Dependency Id="Microsoft.VisualStudio.MPF.15.0" DisplayName="Visual Studio MPF 15.0" d:Source="Installed" Version="[15.0]" />
</Dependencies>
<Prerequisites>
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[15.0)" DisplayName="Visual Studio core editor" />
</Prerequisites>
<Assets>
<Asset Type="Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="%CurrentProject%" Path="|%CurrentProject%|" />
</Assets>
</PackageManifest>

View File

@ -1,9 +1,12 @@
<Project>
<PropertyGroup>
<VersionPrefix>2.1.0</VersionPrefix>
<VsixVersion>15.6</VsixVersion>
<VersionSuffix>preview1</VersionSuffix>
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' == 'rtm' ">$(VersionPrefix)</PackageVersion>
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' != 'rtm' ">$(VersionPrefix)-$(VersionSuffix)-final</PackageVersion>
<VsixVersion Condition="'$(BuildNumber)' != ''">$(VsixVersion).$(BuildNumber)</VsixVersion>
<VsixVersion Condition="'$(BuildNumber)' == ''">$(VsixVersion).999999</VsixVersion>
<BuildNumber Condition="'$(BuildNumber)' == ''">t000</BuildNumber>
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
</PropertyGroup>