-
Notifications
You must be signed in to change notification settings - Fork 229
/
Copy pathParameterExtensions.cs
117 lines (103 loc) · 5.2 KB
/
ParameterExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using System.Diagnostics;
using System.Reflection;
using Aspire.Hosting.Publishing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.Hosting;
namespace Aspire.Hosting;
internal static class ParameterExtensions
{
/// <summary>
/// Creates a password parameter that in the development environment is generated once and stored in the user secrets store.
/// </summary>
/// <remarks>
/// The password is only stable when in the development environment and the application is running. In all other cases, the password is generated each time.
/// </remarks>
public static IResourceBuilder<ParameterResource> CreateStablePassword(this IDistributedApplicationBuilder builder, string name,
bool lower = true, bool upper = true, bool numeric = true, bool special = true,
int minLower = 0, int minUpper = 0, int minNumeric = 0, int minSpecial = 0)
{
ParameterDefault generatedPassword = new GenerateParameterDefault
{
MinLength = 22, // enough to give 128 bits of entropy when using the default 67 possible characters. See remarks in PasswordGenerator.Generate
Lower = lower,
Upper = upper,
Numeric = numeric,
Special = special,
MinLower = minLower,
MinUpper = minUpper,
MinNumeric = minNumeric,
MinSpecial = minSpecial
};
if (builder.Environment.IsDevelopment() && builder.ExecutionContext.IsRunMode)
{
// In development mode, generate a new password each time the application starts
generatedPassword = new UserSecretsParameterDefault(builder.Environment.ApplicationName, name, generatedPassword);
}
var parameterResource = new ParameterResource(name, parameterDefault => GetParameterValue(builder.Configuration, name, parameterDefault), true)
{
Default = generatedPassword
};
return ResourceBuilder.Create(parameterResource, builder);
}
private static string GetParameterValue(IConfiguration configuration, string name, ParameterDefault? parameterDefault)
{
var configurationKey = $"Parameters:{name}";
return configuration[configurationKey]
?? parameterDefault?.GetDefaultValue()
?? throw new DistributedApplicationException($"Parameter resource could not be used because configuration key '{configurationKey}' is missing and the Parameter has no default value."); ;
}
class UserSecretsParameterDefault(string applicationName, string parameterName, ParameterDefault parameterDefault) : ParameterDefault
{
public override string GetDefaultValue()
{
var value = parameterDefault.GetDefaultValue();
var configurationKey = $"Parameters:{parameterName}";
TrySetUserSecret(applicationName, configurationKey, value);
return value;
}
public override void WriteToManifest(ManifestPublishingContext context) => parameterDefault.WriteToManifest(context);
private static bool TrySetUserSecret(string applicationName, string name, string value)
{
if (!string.IsNullOrEmpty(applicationName))
{
var appAssembly = Assembly.Load(new AssemblyName(applicationName));
if (appAssembly is not null && appAssembly.GetCustomAttribute<UserSecretsIdAttribute>()?.UserSecretsId is { } userSecretsId)
{
// Save the value to the secret store
try
{
var startInfo = new ProcessStartInfo
{
FileName = "dotnet",
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
new List<string>(["user-secrets", "set", name, value, "--id", userSecretsId]).ForEach(startInfo.ArgumentList.Add);
var setUserSecrets = Process.Start(startInfo);
setUserSecrets?.WaitForExit(TimeSpan.FromSeconds(10));
return setUserSecrets?.ExitCode == 0;
}
catch (Exception) { }
}
}
return false;
}
}
class ResourceBuilder
{
public static IResourceBuilder<T> Create<T>(T resource, IDistributedApplicationBuilder distributedApplicationBuilder) where T : IResource
{
return new ResourceBuilder<T>(resource, distributedApplicationBuilder);
}
}
class ResourceBuilder<T>(T resource, IDistributedApplicationBuilder distributedApplicationBuilder) : IResourceBuilder<T> where T : IResource
{
public IDistributedApplicationBuilder ApplicationBuilder { get; } = distributedApplicationBuilder;
public T Resource { get; } = resource;
public IResourceBuilder<T> WithAnnotation<TAnnotation>(TAnnotation annotation, ResourceAnnotationMutationBehavior behavior = ResourceAnnotationMutationBehavior.Append) where TAnnotation : IResourceAnnotation
{
throw new NotImplementedException();
}
}
}