-
Notifications
You must be signed in to change notification settings - Fork 3
/
HttpLoggingConfiguration.cs
148 lines (137 loc) · 5.62 KB
/
HttpLoggingConfiguration.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using System;
using System.Collections.Generic;
using System.Linq;
namespace APIMatic.Core.Utilities.Logger.Configuration
{
/// <summary>
/// Abstract class representing configuration settings for HTTP request/response logging.
/// </summary>
public abstract class HttpLoggingConfiguration
{
protected HttpLoggingConfiguration(bool body, bool headers, IReadOnlyCollection<string> headersToInclude,
IReadOnlyCollection<string> headersToExclude, IReadOnlyCollection<string> headersToUnmask)
{
Body = body;
Headers = headers;
HeadersToInclude = headersToInclude;
HeadersToExclude = headersToExclude;
HeadersToUnmask = headersToUnmask;
}
/// <summary>
/// List of non-sensitive headers for unmasking.
/// </summary>
private static readonly List<string> NonSensitiveHeaders = new List<string>
{
"Accept",
"Accept-Charset",
"Accept-Encoding",
"Accept-Language",
"Access-Control-Allow-Origin",
"Cache-Control",
"Connection",
"Content-Encoding",
"Content-Language",
"Content-Length",
"Content-Location",
"Content-MD5",
"Content-Range",
"Content-Type",
"Date",
"ETag",
"Expect",
"Expires",
"From",
"Host",
"If-Match",
"If-Modified-Since",
"If-None-Match",
"If-Range",
"If-Unmodified-Since",
"Keep-Alive",
"Last-Modified",
"Location",
"Max-Forwards",
"Pragma",
"Range",
"Referer",
"Retry-After",
"Server",
"Trailer",
"Transfer-Encoding",
"Upgrade",
"User-Agent",
"Vary",
"Via",
"Warning",
"X-Forwarded-For",
"X-Requested-With",
"X-Powered-By"
};
/// <summary>
/// Gets or sets a value indicating whether to log the body of the HTTP request/response.
/// </summary>
public bool Body { get; }
/// <summary>
/// Gets or sets a value indicating whether to log the headers of the HTTP request/response.
/// </summary>
public bool Headers { get; }
/// <summary>
/// Gets or sets the collection of headers to include in the logged output.
/// </summary>
public IReadOnlyCollection<string> HeadersToInclude { get; }
/// <summary>
/// Gets or sets the collection of headers to exclude from the logged output.
/// </summary>
public IReadOnlyCollection<string> HeadersToExclude { get; }
/// <summary>
/// Gets or sets the collection of headers to unmask (e.g., replace sensitive data) in the logged output.
/// </summary>
public IReadOnlyCollection<string> HeadersToUnmask { get; }
/// <summary>
/// Retrieves the headers to be logged based on the logging configuration, headers, and sensitivity
/// masking configuration.
/// </summary>
/// <param name="headers">The headers to be evaluated for logging.</param>
/// <param name="maskSensitiveHeaders">Determines whether sensitive headers should be masked in the
/// log.</param>
/// <returns>Headers to be logged, considering the provided configuration and sensitivity masking.</returns>
internal IEnumerable<KeyValuePair<string, string>> ExtractHeadersToLog(IDictionary<string, string> headers,
bool maskSensitiveHeaders)
{
IEnumerable<KeyValuePair<string, string>> headersToLog;
if (HeadersToInclude.Any())
{
headersToLog = headers.Where(h => HeadersToInclude.Contains(h.Key, StringComparer.OrdinalIgnoreCase));
}
else if (HeadersToExclude.Any())
headersToLog = headers.Where(h => !HeadersToExclude.Contains(h.Key, StringComparer.OrdinalIgnoreCase));
else
headersToLog = headers;
return MaskSensitiveHeaders(headersToLog, maskSensitiveHeaders);
}
/// <summary>
/// Mask sensitive headers from the given headers.
/// </summary>
/// <param name="headers">The list of headers to filter.</param>
/// <param name="maskSensitiveHeaders">The list of headers to unmask.</param>
/// <returns>Masked headers.</returns>
private IDictionary<string, string> MaskSensitiveHeaders(IEnumerable<KeyValuePair<string, string>> headers,
bool maskSensitiveHeaders)
{
if (!maskSensitiveHeaders) return headers.ToDictionary(h => h.Key, h => h.Value);
return headers.Select(h => new { h.Key, Value = MaskIfHeaderIsSensitive(h.Key, h.Value) })
.ToDictionary(h => h.Key, h => h.Value);
}
/// <summary>
/// Mask the header value if not found in NonSensitiveHeaders and HeadersToUnmask.
/// </summary>
/// <param name="key">Header key.</param>
/// <param name="value">Header Value.</param>
/// <returns>The masked header value or "**Redacted**" if the header is sensitive.</returns>
private string MaskIfHeaderIsSensitive(string key, string value) =>
NonSensitiveHeaders.Contains(key, StringComparer.OrdinalIgnoreCase) ||
HeadersToUnmask.Contains(key, StringComparer.OrdinalIgnoreCase)
? value
: "**Redacted**";
}
}