File: ApplicationModel\ParameterDefault.cs
Web Access
Project: src\src\Aspire.Hosting\Aspire.Hosting.csproj (Aspire.Hosting)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.Hosting.Publishing;
using Aspire.Hosting.Utils;
 
namespace Aspire.Hosting.ApplicationModel;
 
/// <summary>
/// Represents how a default value should be retrieved.
/// </summary>
public abstract class ParameterDefault
{
    /// <summary>
    /// Writes the current <see cref="ParameterDefault"/> to the manifest context.
    /// </summary>
    /// <param name="context">The context for the manifest publishing operation.</param>
    public abstract void WriteToManifest(ManifestPublishingContext context);
 
    /// <summary>
    /// Generates a value for the parameter.
    /// </summary>
    /// <returns>The generated string value.</returns>
    public abstract string GetDefaultValue();
}
 
/// <summary>
/// Represents that a default value should be generated.
/// </summary>
/// <remarks>
/// The recommended minimum bits of entropy for a generated password is 128 bits.
///
/// <para>
/// The general calculation of bits of entropy is:
/// </para>
///
/// <c>log base 2 (numberPossibleOutputs)</c>
///
/// <para>
/// This generator uses 23 upper case, 23 lower case (excludes i,l,o,I,L,O to prevent confusion),
/// 10 numeric, and 11 special characters. So a total of 67 possible characters.
/// </para>
/// 
/// <para>
/// When all character sets are enabled, the number of possible outputs is <c>(67 ^ length)</c>.
/// The minimum password length for 128 bits of entropy is 22 characters: <c>log base 2 (67 ^ 22)</c>.
/// </para>
///
/// <para>
/// When character sets are disabled, it lowers the number of possible outputs and thus the bits of entropy.
/// </para>
///
/// <para>
/// Using MinLower, MinUpper, MinNumeric, and MinSpecial also lowers the number of possible outputs and thus the bits of entropy.
/// </para>
/// 
/// <para>
/// A generalized lower-bound formula for the number of possible outputs is to consider a string of the form:
/// </para>
///
/// <code lang="csharp">
/// {nonRequiredCharacters}{requiredCharacters}
///
/// let a = MinLower, b = MinUpper, c = MinNumeric, d = MinSpecial
/// let x = length - (a + b + c + d)
///
/// nonRequiredPossibilities = 67^x
/// requiredPossibilities = 23^a * 23^b * 10^c * 11^d * (a + b + c + d)! / (a! * b! * c! * d!)
/// 
/// lower-bound of total possibilities = nonRequiredPossibilities * requiredPossibilities
/// </code>
///
/// Putting it all together, the lower-bound bits of entropy calculation is:
///
/// <code lang="csharp">
/// log base 2 [67^x * 23^a * 23^b * 10^c * 11^d * (a + b + c + d)! / (a! * b! * c! * d!)]
/// </code>
/// </remarks>
public sealed class GenerateParameterDefault : ParameterDefault
{
    /// <summary>
    /// Gets or sets the minimum length of the generated value.
    /// </summary>
    public int MinLength { get; set; }
 
    /// <summary>
    /// Gets or sets a value indicating whether to include lowercase alphabet characters in the result.
    /// </summary>
    public bool Lower { get; set; } = true;
 
    /// <summary>
    /// Gets or sets a value indicating whether to include uppercase alphabet characters in the result.
    /// </summary>
    public bool Upper { get; set; } = true;
 
    /// <summary>
    /// Gets or sets a value indicating whether to include numeric characters in the result.
    /// </summary>
    public bool Numeric { get; set; } = true;
 
    /// <summary>
    /// Gets or sets a value indicating whether to include special characters in the result.
    /// </summary>
    public bool Special { get; set; } = true;
 
    /// <summary>
    /// Gets or sets the minimum number of lowercase characters in the result.
    /// </summary>
    public int MinLower { get; set; }
 
    /// <summary>
    /// Gets or sets the minimum number of uppercase characters in the result.
    /// </summary>
    public int MinUpper { get; set; }
 
    /// <summary>
    /// Gets or sets the minimum number of numeric characters in the result.
    /// </summary>
    public int MinNumeric { get; set; }
 
    /// <summary>
    /// Gets or sets the minimum number of special characters in the result.
    /// </summary>
    public int MinSpecial { get; set; }
 
    /// <inheritdoc/>
    public override void WriteToManifest(ManifestPublishingContext context)
    {
        context.Writer.WriteStartObject("generate");
        context.Writer.WriteNumber("minLength", MinLength);
 
        static void WriteBoolIfNotTrue(ManifestPublishingContext context, string propertyName, bool value)
        {
            if (value != true)
            {
                context.Writer.WriteBoolean(propertyName, value);
            }
        }
 
        WriteBoolIfNotTrue(context, "lower", Lower);
        WriteBoolIfNotTrue(context, "upper", Upper);
        WriteBoolIfNotTrue(context, "numeric", Numeric);
        WriteBoolIfNotTrue(context, "special", Special);
 
        static void WriteIntIfNotZero(ManifestPublishingContext context, string propertyName, int value)
        {
            if (value != 0)
            {
                context.Writer.WriteNumber(propertyName, value);
            }
        }
 
        WriteIntIfNotZero(context, "minLower", MinLower);
        WriteIntIfNotZero(context, "minUpper", MinUpper);
        WriteIntIfNotZero(context, "minNumeric", MinNumeric);
        WriteIntIfNotZero(context, "minSpecial", MinSpecial);
 
        context.Writer.WriteEndObject();
    }
 
    /// <inheritdoc/>
    public override string GetDefaultValue() =>
        PasswordGenerator.Generate(MinLength, Lower, Upper, Numeric, Special, MinLower, MinUpper, MinNumeric, MinSpecial);
}