Skip to content

Commit fe2efcf

Browse files
committed
Create CircuitSecret
Fixes: #13012 This change introduces CircuitSecret as the concept that's used when doing handshaking between the client and the server, and makes CircuitId (visible to user-code) a separate concept (not a secret, can't be used to open a connection). The scope of this grew once I realized that we probably shouldn't be logging Circuit Secret in so many places, we should be logging CircuitId as the piece of data we use for correlation, and try to keep the secret out of logs except where really necessary (and with trace level). I ended up creating a new type to represent the secret and prevent accidental misuse, and then chased down all of the build errors.
1 parent 10452bf commit fe2efcf

17 files changed

+315
-238
lines changed

src/Components/Server/src/CircuitDisconnectMiddleware.cs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ internal class CircuitDisconnectMiddleware
1717
public CircuitDisconnectMiddleware(
1818
ILogger<CircuitDisconnectMiddleware> logger,
1919
CircuitRegistry registry,
20-
CircuitIdFactory circuitIdFactory,
20+
CircuitSecretFactory circuitSecretFactory,
2121
RequestDelegate next)
2222
{
2323
Logger = logger;
2424
Registry = registry;
25-
CircuitIdFactory = circuitIdFactory;
25+
CircuitSecretFactory = circuitSecretFactory;
2626
Next = next;
2727
}
2828

2929
public ILogger<CircuitDisconnectMiddleware> Logger { get; }
3030
public CircuitRegistry Registry { get; }
31-
public CircuitIdFactory CircuitIdFactory { get; }
31+
public CircuitSecretFactory CircuitSecretFactory { get; }
3232
public RequestDelegate Next { get; }
3333

3434
public async Task Invoke(HttpContext context)
@@ -39,65 +39,66 @@ public async Task Invoke(HttpContext context)
3939
return;
4040
}
4141

42-
var (hasCircuitId, circuitId) = await TryGetCircuitIdAsync(context);
43-
if (!hasCircuitId)
42+
var circuitSecret = await GetCircuitSecretAsync(context);
43+
if (circuitSecret is null)
4444
{
4545
context.Response.StatusCode = StatusCodes.Status400BadRequest;
4646
return;
4747
}
4848

49-
await TerminateCircuitGracefully(circuitId);
49+
await TerminateCircuitGracefully(circuitSecret.Value);
5050

5151
context.Response.StatusCode = StatusCodes.Status200OK;
5252
}
5353

54-
private async Task<(bool, string)> TryGetCircuitIdAsync(HttpContext context)
54+
private async Task<CircuitSecret?> GetCircuitSecretAsync(HttpContext context)
5555
{
5656
try
5757
{
5858
if (!context.Request.HasFormContentType)
5959
{
60-
return (false, null);
60+
return default;
6161
}
6262

6363
var form = await context.Request.ReadFormAsync();
64-
if (!form.TryGetValue(CircuitIdKey, out var circuitId) || !CircuitIdFactory.ValidateCircuitId(circuitId))
64+
if (!form.TryGetValue(CircuitIdKey, out var text))
6565
{
66-
return (false, null);
66+
return default;
6767
}
6868

69-
return (true, circuitId);
69+
var circuitSecret = new CircuitSecret(text);
70+
if (!CircuitSecretFactory.ValidateCircuitSecret(circuitSecret))
71+
{
72+
return default;
73+
}
74+
75+
return circuitSecret;
7076
}
7177
catch
7278
{
73-
return (false, null);
79+
return default;
7480
}
7581
}
7682

77-
private async Task TerminateCircuitGracefully(string circuitId)
83+
private async Task TerminateCircuitGracefully(CircuitSecret circuitSecret)
7884
{
79-
try
80-
{
81-
await Registry.TerminateAsync(circuitId);
82-
Log.CircuitTerminatedGracefully(Logger, circuitId);
83-
}
84-
catch (Exception e)
85-
{
86-
Log.UnhandledExceptionInCircuit(Logger, circuitId, e);
87-
}
85+
// We don't expect TerminateAsync to throw.
86+
Log.CircuitTerminatingGracefully(Logger, circuitSecret);
87+
await Registry.TerminateAsync(circuitSecret);
88+
Log.CircuitTerminatedGracefully(Logger, circuitSecret);
8889
}
8990

9091
private class Log
9192
{
92-
private static readonly Action<ILogger, string, Exception> _circuitTerminatedGracefully =
93-
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(1, "CircuitTerminatedGracefully"), "Circuit '{CircuitId}' terminated gracefully");
93+
private static readonly Action<ILogger, string, Exception> _circuitTerminatingGracefully =
94+
LoggerMessage.Define<string>(LogLevel.Trace, new EventId(1, "CircuitTerminatingGracefully"), "Circuit '{CircuitSecret}' terminating gracefully.");
9495

95-
private static readonly Action<ILogger, string, Exception> _unhandledExceptionInCircuit =
96-
LoggerMessage.Define<string>(LogLevel.Warning, new EventId(2, "UnhandledExceptionInCircuit"), "Unhandled exception in circuit {CircuitId} while terminating gracefully.");
96+
private static readonly Action<ILogger, string, Exception> _circuitTerminatedGracefully =
97+
LoggerMessage.Define<string>(LogLevel.Trace, new EventId(2, "CircuitTerminatedGracefully"), "Circuit '{CircuitSecret}' terminated gracefully.");
9798

98-
public static void CircuitTerminatedGracefully(ILogger logger, string circuitId) => _circuitTerminatedGracefully(logger, circuitId, null);
99+
public static void CircuitTerminatingGracefully(ILogger logger, CircuitSecret circuitSecret) => _circuitTerminatingGracefully(logger, circuitSecret.Secret, null);
99100

100-
public static void UnhandledExceptionInCircuit(ILogger logger, string circuitId, Exception exception) => _unhandledExceptionInCircuit(logger, circuitId, exception);
101+
public static void CircuitTerminatedGracefully(ILogger logger, CircuitSecret circuitSecret) => _circuitTerminatedGracefully(logger, circuitSecret.Secret, null);
101102
}
102103
}
103104
}

src/Components/Server/src/Circuits/CircuitHost.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ internal class CircuitHost : IAsyncDisposable
4040

4141
public CircuitHost(
4242
string circuitId,
43+
CircuitSecret circuitSecret,
4344
IServiceScope scope,
4445
CircuitOptions options,
4546
CircuitClientProxy client,
@@ -50,6 +51,12 @@ public CircuitHost(
5051
ILogger logger)
5152
{
5253
CircuitId = circuitId ?? throw new ArgumentNullException(nameof(circuitId));
54+
CircuitSecret = circuitSecret;
55+
if (circuitSecret.Secret is null)
56+
{
57+
// Prevent the use of a 'default' secret.
58+
throw new ArgumentException(nameof(circuitSecret));
59+
}
5360

5461
_scope = scope ?? throw new ArgumentNullException(nameof(scope));
5562
_options = options ?? throw new ArgumentNullException(nameof(options));
@@ -71,8 +78,18 @@ public CircuitHost(
7178

7279
public CircuitHandle Handle { get; }
7380

81+
/// <summary>
82+
/// Gets a reference to the Circuit ID. This is an identifier that can be used safely in server-side
83+
/// code to identify the circuit for logging, correlation or remote storage.
84+
/// </summary>
7485
public string CircuitId { get; }
7586

87+
/// <summary>
88+
/// Gets a reference to the Circuit Secret. This is an identifier that is used to make the handshake
89+
/// between the client and server. This should not exposed to user-code.
90+
/// </summary>
91+
public CircuitSecret CircuitSecret { get; }
92+
7693
public Circuit Circuit { get; }
7794

7895
public CircuitClientProxy Client { get; set; }
@@ -648,7 +665,7 @@ private static class EventIds
648665
public static readonly EventId LocationChangeSucceded = new EventId(209, "LocationChangeSucceeded");
649666
public static readonly EventId LocationChangeFailed = new EventId(210, "LocationChangeFailed");
650667
public static readonly EventId LocationChangeFailedInCircuit = new EventId(211, "LocationChangeFailedInCircuit");
651-
public static readonly EventId OnRenderCompletedFailed = new EventId(212, " OnRenderCompletedFailed");
668+
public static readonly EventId OnRenderCompletedFailed = new EventId(212, "OnRenderCompletedFailed");
652669
}
653670

654671
static Log()

0 commit comments

Comments
 (0)