Skip to content

Commit 1ee882b

Browse files
committed
Address feedback from review
1 parent 81a96f0 commit 1ee882b

13 files changed

+131
-114
lines changed

eng/ProjectReferences.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
<ProjectReferenceProvider Include="Microsoft.AspNetCore.CookiePolicy" ProjectPath="$(RepoRoot)src\Security\CookiePolicy\src\Microsoft.AspNetCore.CookiePolicy.csproj" />
7070
<ProjectReferenceProvider Include="Microsoft.Web.Xdt.Extensions" ProjectPath="$(RepoRoot)src\SiteExtensions\Microsoft.Web.Xdt.Extensions\src\Microsoft.Web.Xdt.Extensions.csproj" />
7171
<ProjectReferenceProvider Include="dotnet-getdocument" ProjectPath="$(RepoRoot)src\Tools\dotnet-getdocument\src\dotnet-getdocument.csproj" />
72-
<ProjectReferenceProvider Include="dotnet-user-secrets" ProjectPath="$(RepoRoot)src\Tools\dotnet-user-secrets\src\dotnet-user-secrets.csproj" />
7372
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Client" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Client\src\Microsoft.Extensions.ApiDescription.Client.csproj" />
7473
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Server" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Server\src\Microsoft.Extensions.ApiDescription.Server.csproj" />
7574
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" ProjectPath="$(RepoRoot)src\Tools\FirstRunCertGenerator\src\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj" />

src/Tools/dotnet-user-secrets/src/Internal/MsBuildProjectFinder.cs renamed to src/Tools/Shared/SecretsHelpers/MsBuildProjectFinder.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using System.IO;
64
using System.Linq;
75
using Microsoft.Extensions.Tools.Internal;
86

@@ -36,20 +34,20 @@ public string FindMsBuildProject(string project)
3634

3735
if (projects.Count > 1)
3836
{
39-
throw new FileNotFoundException(Resources.FormatError_MultipleProjectsFound(projectPath));
37+
throw new FileNotFoundException($"Multiple MSBuild project files found in '{projectPath}'. Specify which to use with the --project option.");
4038
}
4139

4240
if (projects.Count == 0)
4341
{
44-
throw new FileNotFoundException(Resources.FormatError_NoProjectsFound(projectPath));
42+
throw new FileNotFoundException($"Could not find a MSBuild project file in '{projectPath}'. Specify which project to use with the --project option.");
4543
}
4644

4745
return projects[0];
4846
}
4947

5048
if (!File.Exists(projectPath))
5149
{
52-
throw new FileNotFoundException(Resources.FormatError_ProjectPath_NotFound(projectPath));
50+
throw new FileNotFoundException($"The project file '{projectPath}' does not exist.");
5351
}
5452

5553
return projectPath;

src/Tools/dotnet-user-secrets/src/Internal/ProjectIdResolver.cs renamed to src/Tools/Shared/SecretsHelpers/ProjectIdResolver.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal;
1515
/// This API supports infrastructure and is not intended to be used
1616
/// directly from your code. This API may change or be removed in future releases.
1717
/// </summary>
18-
public class ProjectIdResolver
18+
internal sealed class ProjectIdResolver
1919
{
2020
private const string DefaultConfig = "Debug";
2121
private readonly IReporter _reporter;
@@ -32,9 +32,19 @@ public ProjectIdResolver(IReporter reporter, string workingDirectory)
3232
public string Resolve(string project, string configuration)
3333
{
3434
var finder = new MsBuildProjectFinder(_workingDirectory);
35-
var projectFile = finder.FindMsBuildProject(project);
35+
string projectFile;
36+
try
37+
{
38+
projectFile = finder.FindMsBuildProject(project);
39+
}
40+
catch (Exception ex)
41+
{
42+
_reporter.Error(ex.Message);
43+
return null;
44+
}
45+
3646

37-
_reporter.Verbose(Resources.FormatMessage_Project_File_Path(projectFile));
47+
_reporter.Verbose($"Project file path {projectFile}.");
3848

3949
configuration = !string.IsNullOrEmpty(configuration)
4050
? configuration
@@ -98,18 +108,20 @@ public string Resolve(string project, string configuration)
98108
_reporter.Verbose(outputBuilder.ToString());
99109
_reporter.Verbose(errorBuilder.ToString());
100110
_reporter.Error($"Exit code: {process.ExitCode}");
101-
throw new InvalidOperationException(Resources.FormatError_ProjectFailedToLoad(projectFile));
111+
_reporter.Error($"Could not load the MSBuild project '{projectFile}'.");
112+
return null;
102113
}
103114

104115
if (!File.Exists(outputFile))
105116
{
106-
throw new FileNotFoundException(Resources.FormatError_ProjectMissingId(projectFile));
117+
_reporter.Error($"Could not find the global property 'UserSecretsId' in MSBuild project '{projectFile}'. Ensure this property is set in the project or use the '--id' command line option.");
118+
return null;
107119
}
108120

109121
var id = File.ReadAllText(outputFile)?.Trim();
110122
if (string.IsNullOrEmpty(id))
111123
{
112-
throw new NullReferenceException(Resources.FormatError_ProjectMissingId(projectFile));
124+
_reporter.Error($"Could not find the global property 'UserSecretsId' in MSBuild project '{projectFile}'. Ensure this property is set in the project or use the '--id' command line option.");
113125
}
114126
return id;
115127

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Linq;
5+
using System.Xml;
6+
using System.Xml.Linq;
7+
using System.Xml.XPath;
8+
using Microsoft.Extensions.Tools.Internal;
9+
10+
namespace Microsoft.Extensions.SecretManager.Tools.Internal;
11+
12+
internal static class UserSecretsCreator
13+
{
14+
public static string CreateUserSecretsId(IReporter reporter, string project, string workingDirectory, string overrideId = null)
15+
{
16+
var projectPath = ResolveProjectPath(project, workingDirectory);
17+
18+
// Load the project file as XML
19+
var projectDocument = XDocument.Load(projectPath, LoadOptions.PreserveWhitespace);
20+
21+
// Accept the `--id` CLI option to the main app
22+
string newSecretsId = string.IsNullOrWhiteSpace(overrideId)
23+
? Guid.NewGuid().ToString()
24+
: overrideId;
25+
26+
// Confirm secret ID does not contain invalid characters
27+
if (Path.GetInvalidPathChars().Any(invalidChar => newSecretsId.Contains(invalidChar)))
28+
{
29+
throw new ArgumentException($"The UserSecretsId '{newSecretsId}' cannot contain any characters that cannot be used in a file path.");
30+
}
31+
32+
var existingUserSecretsId = projectDocument.XPathSelectElements("//UserSecretsId").FirstOrDefault();
33+
34+
// Check if a UserSecretsId is already set
35+
if (existingUserSecretsId is object)
36+
{
37+
// Only set the UserSecretsId if the user specified an explicit value
38+
if (string.IsNullOrWhiteSpace(overrideId))
39+
{
40+
reporter.Output($"The MSBuild project '{projectPath}' has already been initialized with a UserSecretsId.");
41+
return existingUserSecretsId.Value;
42+
}
43+
44+
existingUserSecretsId.SetValue(newSecretsId);
45+
}
46+
else
47+
{
48+
// Find the first non-conditional PropertyGroup
49+
var propertyGroup = projectDocument.Root.DescendantNodes()
50+
.FirstOrDefault(node => node is XElement el
51+
&& el.Name == "PropertyGroup"
52+
&& el.Attributes().All(attr =>
53+
attr.Name != "Condition")) as XElement;
54+
55+
// No valid property group, create a new one
56+
if (propertyGroup == null)
57+
{
58+
propertyGroup = new XElement("PropertyGroup");
59+
projectDocument.Root.AddFirst(propertyGroup);
60+
}
61+
62+
// Add UserSecretsId element
63+
propertyGroup.Add(" ");
64+
propertyGroup.Add(new XElement("UserSecretsId", newSecretsId));
65+
propertyGroup.Add($"{Environment.NewLine} ");
66+
}
67+
68+
var settings = new XmlWriterSettings
69+
{
70+
OmitXmlDeclaration = true,
71+
};
72+
73+
using var xw = XmlWriter.Create(projectPath, settings);
74+
projectDocument.Save(xw);
75+
76+
reporter.Output($"Set UserSecretsId to '{newSecretsId}' for MSBuild project '{projectPath}'.");
77+
return newSecretsId;
78+
}
79+
80+
private static string ResolveProjectPath(string name, string path)
81+
{
82+
var finder = new MsBuildProjectFinder(path);
83+
return finder.FindMsBuildProject(name);
84+
}
85+
}

src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@ internal static class DevJwtCliHelpers
1616
public static string GetOrSetUserSecretsId(IReporter reporter, string projectFilePath)
1717
{
1818
var resolver = new ProjectIdResolver(reporter, projectFilePath);
19-
try
19+
var id = resolver.Resolve(projectFilePath, configuration: null);
20+
if (string.IsNullOrEmpty(id))
2021
{
21-
return resolver.Resolve(projectFilePath, configuration: null);
22-
}
23-
catch (NullReferenceException)
24-
{
25-
return InitCommand.CreateUserSecretsId(reporter, projectFilePath, projectFilePath);
22+
return UserSecretsCreator.CreateUserSecretsId(reporter, projectFilePath, projectFilePath);
2623
}
24+
return id;
2725
}
2826

2927
public static string GetProject(string projectPath = null)

src/Tools/dotnet-user-jwts/src/dotnet-user-jwts.csproj

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,15 @@
1313

1414
<ItemGroup>
1515
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" LinkBase="Shared" />
16-
<Compile Include="$(ToolSharedSourceRoot)CommandLine\**\*.cs"
17-
Exclude="$(ToolSharedSourceRoot)CommandLine\*Reporter.cs;$(ToolSharedSourceRoot)CommandLine\*Console.cs"
18-
LinkBase="Shared" />
16+
<Compile Include="$(ToolSharedSourceRoot)CommandLine\**\*.cs" LinkBase="Shared" />
17+
<Compile Include="$(ToolSharedSourceRoot)SecretsHelpers\*.cs" LinkBase="Shared" />
18+
<None Include="$(ToolSharedSourceRoot)\SecretsHelpers\assets\SecretManager.targets" Link="assets\SecretManager.targets" CopyToOutputDirectory="PreserveNewest" />
1919
</ItemGroup>
2020

2121
<ItemGroup>
2222
<Reference Include="System.IdentityModel.Tokens.Jwt" />
2323
<Reference Include="Microsoft.Extensions.Configuration.Abstractions" />
2424
<Reference Include="Microsoft.Extensions.Configuration" />
2525
<Reference Include="Microsoft.Extensions.Configuration.UserSecrets" />
26-
<Reference Include="dotnet-user-secrets" />
2726
</ItemGroup>
2827
</Project>

src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,16 @@ public void List_HandlesNoSecretsInProject()
5050
}
5151

5252
[Fact]
53-
public void Create_WarnsOnNoSecretInproject()
53+
public void Create_CreatesSecretOnNoSecretInproject()
5454
{
5555
var project = Path.Combine(_fixture.CreateProject(false), "TestProject.csproj");
5656
var app = new Program(_console);
5757

5858
app.Run(new[] { "create", "--project", project });
59-
Assert.Contains("Set UserSecretsId to ", _console.GetOutput());
60-
Assert.Contains("New JWT saved", _console.GetOutput());
59+
var output = _console.GetOutput();
60+
Assert.DoesNotContain("could not find SecretManager.targets", output);
61+
Assert.Contains("Set UserSecretsId to ", output);
62+
Assert.Contains("New JWT saved", output);
6163
}
6264

6365
[Fact]

src/Tools/dotnet-user-jwts/test/dotnet-user-jwts.Tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77

88
<ItemGroup>
99
<Compile Include="$(ToolSharedSourceRoot)TestHelpers\**\*.cs" />
10+
<Content Include="$(ToolSharedSourceRoot)\SecretsHelpers\assets\SecretManager.targets" Link="assets\SecretManager.targets" CopyToOutputDirectory="PreserveNewest" />
1011
</ItemGroup>
1112

1213
<ItemGroup>
1314
<ProjectReference Include="..\src\dotnet-user-jwts.csproj" />
1415
</ItemGroup>
1516

16-
</Project>
17+
</Project>
Lines changed: 1 addition & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Linq;
5-
using System.Xml;
6-
using System.Xml.Linq;
7-
using System.Xml.XPath;
84
using Microsoft.Extensions.CommandLineUtils;
9-
using Microsoft.Extensions.Tools.Internal;
105

116
namespace Microsoft.Extensions.SecretManager.Tools.Internal;
127

@@ -72,78 +67,6 @@ public void Execute(CommandContext context, string workingDirectory)
7267

7368
public void Execute(CommandContext context)
7469
{
75-
CreateUserSecretsId(context.Reporter, ProjectPath, WorkingDirectory, OverrideId);
76-
}
77-
78-
public static string CreateUserSecretsId(IReporter reporter, string project, string workingDirectory, string overrideId = null)
79-
{
80-
var projectPath = ResolveProjectPath(project, workingDirectory);
81-
82-
// Load the project file as XML
83-
var projectDocument = XDocument.Load(projectPath, LoadOptions.PreserveWhitespace);
84-
85-
// Accept the `--id` CLI option to the main app
86-
string newSecretsId = string.IsNullOrWhiteSpace(overrideId)
87-
? Guid.NewGuid().ToString()
88-
: overrideId;
89-
90-
// Confirm secret ID does not contain invalid characters
91-
if (Path.GetInvalidPathChars().Any(invalidChar => newSecretsId.Contains(invalidChar)))
92-
{
93-
throw new ArgumentException(Resources.FormatError_InvalidSecretsId(newSecretsId));
94-
}
95-
96-
var existingUserSecretsId = projectDocument.XPathSelectElements("//UserSecretsId").FirstOrDefault();
97-
98-
// Check if a UserSecretsId is already set
99-
if (existingUserSecretsId is object)
100-
{
101-
// Only set the UserSecretsId if the user specified an explicit value
102-
if (string.IsNullOrWhiteSpace(overrideId))
103-
{
104-
reporter.Output(Resources.FormatMessage_ProjectAlreadyInitialized(projectPath));
105-
return existingUserSecretsId.Value;
106-
}
107-
108-
existingUserSecretsId.SetValue(newSecretsId);
109-
}
110-
else
111-
{
112-
// Find the first non-conditional PropertyGroup
113-
var propertyGroup = projectDocument.Root.DescendantNodes()
114-
.FirstOrDefault(node => node is XElement el
115-
&& el.Name == "PropertyGroup"
116-
&& el.Attributes().All(attr =>
117-
attr.Name != "Condition")) as XElement;
118-
119-
// No valid property group, create a new one
120-
if (propertyGroup == null)
121-
{
122-
propertyGroup = new XElement("PropertyGroup");
123-
projectDocument.Root.AddFirst(propertyGroup);
124-
}
125-
126-
// Add UserSecretsId element
127-
propertyGroup.Add(" ");
128-
propertyGroup.Add(new XElement("UserSecretsId", newSecretsId));
129-
propertyGroup.Add($"{Environment.NewLine} ");
130-
}
131-
132-
var settings = new XmlWriterSettings
133-
{
134-
OmitXmlDeclaration = true,
135-
};
136-
137-
using var xw = XmlWriter.Create(projectPath, settings);
138-
projectDocument.Save(xw);
139-
140-
reporter.Output(Resources.FormatMessage_SetUserSecretsIdForProject(newSecretsId, projectPath));
141-
return newSecretsId;
142-
}
143-
144-
private static string ResolveProjectPath(string name, string path)
145-
{
146-
var finder = new MsBuildProjectFinder(path);
147-
return finder.FindMsBuildProject(name);
70+
UserSecretsCreator.CreateUserSecretsId(context.Reporter, ProjectPath, WorkingDirectory, OverrideId);
14871
}
14972
}

src/Tools/dotnet-user-secrets/src/Program.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,10 @@ internal int RunInternal(params string[] args)
7575
return 0;
7676
}
7777

78-
string userSecretsId;
79-
try
80-
{
81-
userSecretsId = ResolveId(options, reporter);
82-
}
83-
catch (Exception ex) when (ex is InvalidOperationException || ex is FileNotFoundException)
78+
var userSecretsId = ResolveId(options, reporter);
79+
80+
if (string.IsNullOrEmpty(userSecretsId))
8481
{
85-
reporter.Error(ex.Message);
8682
return 1;
8783
}
8884

src/Tools/dotnet-user-secrets/src/dotnet-user-secrets.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
<ItemGroup>
1717
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
1818
<Compile Include="$(ToolSharedSourceRoot)CommandLine\**\*.cs" />
19-
<None Include="assets\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
19+
<Compile Include="$(ToolSharedSourceRoot)SecretsHelpers\*.cs" />
20+
<None Include="$(ToolSharedSourceRoot)\SecretsHelpers\assets\SecretManager.targets" Link="assets\SecretManager.targets" CopyToOutputDirectory="PreserveNewest" />
2021
</ItemGroup>
2122

2223
<ItemGroup>

src/Tools/dotnet-user-secrets/test/dotnet-user-secrets.Tests.csproj

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77

88
<ItemGroup>
99
<Compile Include="$(ToolSharedSourceRoot)TestHelpers\**\*.cs" />
10-
<Content Include="..\src\assets\SecretManager.targets" Link="assets\SecretManager.targets" CopyToOutputDirectory="PreserveNewest" />
10+
<Content Include="$(ToolSharedSourceRoot)\SecretsHelpers\assets\SecretManager.targets" Link="assets\SecretManager.targets" CopyToOutputDirectory="PreserveNewest" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\src\dotnet-user-secrets.csproj" />
1115
</ItemGroup>
1216

1317
<ItemGroup>
1418
<Reference Include="Microsoft.Extensions.Configuration.UserSecrets" />
15-
<Reference Include="dotnet-user-secrets" />
1619
</ItemGroup>
1720

1821
</Project>

0 commit comments

Comments
 (0)