33
44using System ;
55using System . Collections . Generic ;
6- using System . Runtime . CompilerServices ;
6+ using System . Globalization ;
77using System . Threading . Tasks ;
88using Microsoft . AspNetCore . Http ;
99
@@ -28,10 +28,11 @@ public sealed class ImageSharpRequestAuthorizationUtilities
2828 /// The command used by image requests for transporting Hash-based Message Authentication Code (HMAC) tokens.
2929 /// </summary>
3030 public const string TokenCommand = HMACUtilities . TokenCommand ;
31-
3231 private static readonly Uri FallbackBaseUri = new ( "http://localhost/" ) ;
3332 private readonly HashSet < string > knownCommands ;
3433 private readonly ImageSharpMiddlewareOptions options ;
34+ private readonly CommandParser commandParser ;
35+ private readonly CultureInfo parserCulture ;
3536 private readonly IRequestParser requestParser ;
3637 private readonly IServiceProvider serviceProvider ;
3738
@@ -41,18 +42,26 @@ public sealed class ImageSharpRequestAuthorizationUtilities
4142 /// <param name="options">The middleware configuration options.</param>
4243 /// <param name="requestParser">An <see cref="IRequestParser"/> instance used to parse image requests for commands.</param>
4344 /// <param name="processors">A collection of <see cref="IImageWebProcessor"/> instances used to process images.</param>
45+ /// <param name="commandParser">The command parser.</param>
4446 /// <param name="serviceProvider">The service provider.</param>
4547 public ImageSharpRequestAuthorizationUtilities (
4648 IOptions < ImageSharpMiddlewareOptions > options ,
4749 IRequestParser requestParser ,
4850 IEnumerable < IImageWebProcessor > processors ,
51+ CommandParser commandParser ,
4952 IServiceProvider serviceProvider )
5053 {
5154 Guard . NotNull ( options , nameof ( options ) ) ;
5255 Guard . NotNull ( requestParser , nameof ( requestParser ) ) ;
5356 Guard . NotNull ( processors , nameof ( processors ) ) ;
5457 Guard . NotNull ( serviceProvider , nameof ( serviceProvider ) ) ;
5558
59+ this . options = options . Value ;
60+ this . commandParser = commandParser ;
61+ this . parserCulture = this . options . UseInvariantParsingCulture
62+ ? CultureInfo . InvariantCulture
63+ : CultureInfo . CurrentCulture ;
64+
5665 HashSet < string > commands = new ( StringComparer . OrdinalIgnoreCase ) ;
5766 foreach ( IImageWebProcessor processor in processors )
5867 {
@@ -63,7 +72,6 @@ public ImageSharpRequestAuthorizationUtilities(
6372 }
6473
6574 this . knownCommands = commands ;
66- this . options = options . Value ;
6775 }
6876
6977 /// <summary>
@@ -90,91 +98,165 @@ public void StripUnknownCommands(CommandCollection commands)
9098 /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
9199 /// </summary>
92100 /// <param name="uri">The uri to compute the code from.</param>
101+ /// <param name="handling">The command collection handling.</param>
93102 /// <returns>The computed HMAC.</returns>
94- public string ComputeHMAC ( string uri )
95- => this . ComputeHMAC ( new Uri ( uri ) ) ;
103+ public string ComputeHMAC ( string uri , CommandHandling handling )
104+ => this . ComputeHMAC ( new Uri ( uri ) , handling ) ;
96105
97106 /// <summary>
98107 /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
99108 /// </summary>
100109 /// <param name="uri">The uri to compute the code from.</param>
110+ /// <param name="handling">The command collection handling.</param>
101111 /// <returns>The computed HMAC.</returns>
102- public string ComputeHMAC ( Uri uri )
103- => AsyncHelper . RunSync ( ( ) => this . ComputeHMACAsync ( uri ) ) ;
112+ public Task < string > ComputeHMACAsync ( string uri , CommandHandling handling )
113+ => this . ComputeHMACAsync ( new Uri ( uri ) , handling ) ;
104114
105115 /// <summary>
106116 /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
107117 /// </summary>
108118 /// <param name="uri">The uri to compute the code from.</param>
119+ /// <param name="handling">The command collection handling.</param>
109120 /// <returns>The computed HMAC.</returns>
110- public Task < string > ComputeHMACAsync ( string uri )
111- => this . ComputeHMACAsync ( new Uri ( uri ) ) ;
121+ public string ComputeHMAC ( Uri uri , CommandHandling handling )
122+ {
123+ ToComponents (
124+ uri ,
125+ out HostString host ,
126+ out PathString path ,
127+ out QueryString queryString ) ;
128+
129+ return this . ComputeHMAC ( host , path , queryString , handling ) ;
130+ }
112131
113132 /// <summary>
114133 /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
115134 /// </summary>
116135 /// <param name="uri">The uri to compute the code from.</param>
136+ /// <param name="handling">The command collection handling.</param>
117137 /// <returns>The computed HMAC.</returns>
118- public async Task < string > ComputeHMACAsync ( Uri uri )
138+ public Task < string > ComputeHMACAsync ( Uri uri , CommandHandling handling )
119139 {
120- byte [ ] secret = this . options . HMACSecretKey ;
121- if ( secret is null || secret . Length == 0 )
122- {
123- return null ;
124- }
125-
126- // We need to generate a HttpRequest to use the rest of the services.
127- DefaultHttpContext context = new ( ) { RequestServices = this . serviceProvider } ;
128- HttpRequest request = context . Request ;
129-
130140 ToComponents (
131141 uri ,
132142 out HostString host ,
133143 out PathString path ,
134- out QueryString queryString ,
135- out QueryCollection query ) ;
144+ out QueryString queryString ) ;
136145
137- request . Host = host ;
138- request . Path = path ;
139- request . QueryString = queryString ;
140- request . Query = query ;
146+ return this . ComputeHMACAsync ( host , path , queryString , handling ) ;
147+ }
141148
142- CommandCollection commands = this . requestParser . ParseRequestCommands ( context ) ;
149+ /// <summary>
150+ /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
151+ /// </summary>
152+ /// <param name="host">The host header.</param>
153+ /// <param name="path">The path or pathbase.</param>
154+ /// <param name="queryString">The querystring.</param>
155+ /// <param name="handling">The command collection handling.</param>
156+ /// <returns>The computed HMAC.</returns>
157+ public string ComputeHMAC ( HostString host , PathString path , QueryString queryString , CommandHandling handling )
158+ => this . ComputeHMAC ( host , path , queryString , handling ) ;
143159
144- // The provided URI should not contain invalid commands since image URI geneneration
145- // should be tightly controlled by the running application but we will strip any out anyway.
146- this . StripUnknownCommands ( commands ) ;
160+ /// <summary>
161+ /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
162+ /// </summary>
163+ /// <param name="host">The host header.</param>
164+ /// <param name="path">The path or pathbase.</param>
165+ /// <param name="queryString">The querystring.</param>
166+ /// <param name="handling">The command collection handling.</param>
167+ /// <returns>The computed HMAC.</returns>
168+ public Task < string > ComputeHMACAsync ( HostString host , PathString path , QueryString queryString , CommandHandling handling )
169+ => this . ComputeHMACAsync ( host , path , queryString , new ( QueryHelpers . ParseQuery ( queryString . Value ) ) , handling ) ;
147170
148- ImageCommandContext imageCommandContext = new ( context , commands , null , null ) ;
171+ /// <summary>
172+ /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
173+ /// </summary>
174+ /// <param name="host">The host header.</param>
175+ /// <param name="path">The path or pathbase.</param>
176+ /// <param name="queryString">The querystring.</param>
177+ /// <param name="query">The query collection.</param>
178+ /// <param name="handling">The command collection handling.</param>
179+ /// <returns>The computed HMAC.</returns>
180+ public string ComputeHMAC ( HostString host , PathString path , QueryString queryString , QueryCollection query , CommandHandling handling )
181+ => this . ComputeHMAC ( this . ToHttpContext ( host , path , queryString , query ) , handling ) ;
182+
183+ /// <summary>
184+ /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
185+ /// </summary>
186+ /// <param name="host">The host header.</param>
187+ /// <param name="path">The path or pathbase.</param>
188+ /// <param name="queryString">The querystring.</param>
189+ /// <param name="query">The query collection.</param>
190+ /// <param name="handling">The command collection handling.</param>
191+ /// <returns>The computed HMAC.</returns>
192+ public Task < string > ComputeHMACAsync ( HostString host , PathString path , QueryString queryString , QueryCollection query , CommandHandling handling )
193+ => this . ComputeHMACAsync ( this . ToHttpContext ( host , path , queryString , query ) , handling ) ;
194+
195+ /// <summary>
196+ /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
197+ /// </summary>
198+ /// <param name="context">The request HTTP context.</param>
199+ /// <param name="handling">The command collection handling.</param>
200+ /// <returns>The computed HMAC.</returns>
201+ public string ComputeHMAC ( HttpContext context , CommandHandling handling )
202+ => AsyncHelper . RunSync ( ( ) => this . ComputeHMACAsync ( context , handling ) ) ;
203+
204+ /// <summary>
205+ /// Compute a Hash-based Message Authentication Code (HMAC) for request authentication.
206+ /// </summary>
207+ /// <param name="context">The request HTTP context.</param>
208+ /// <param name="handling">The command collection handling.</param>
209+ /// <returns>The computed HMAC.</returns>
210+ public async Task < string > ComputeHMACAsync ( HttpContext context , CommandHandling handling )
211+ {
212+ byte [ ] secret = this . options . HMACSecretKey ;
213+ if ( secret is null || secret . Length == 0 )
214+ {
215+ return null ;
216+ }
217+
218+ CommandCollection commands = this . requestParser . ParseRequestCommands ( context ) ;
219+ if ( handling == CommandHandling . Sanitize )
220+ {
221+ this . StripUnknownCommands ( commands ) ;
222+ }
223+
224+ ImageCommandContext imageCommandContext = new ( context , commands , this . commandParser , this . parserCulture ) ;
149225 return await this . options . OnComputeHMACAsync ( imageCommandContext , secret ) ;
150226 }
151227
152228 private static void ToComponents (
153229 Uri uri ,
154230 out HostString host ,
155231 out PathString path ,
156- out QueryString queryString ,
157- out QueryCollection query )
232+ out QueryString queryString )
158233 {
159234 if ( uri . IsAbsoluteUri )
160235 {
161236 host = HostString . FromUriComponent ( uri ) ;
162237 path = PathString . FromUriComponent ( uri ) ;
163238 queryString = QueryString . FromUriComponent ( uri ) ;
164- query = GetQueryComponent ( queryString ) ;
165239 }
166240 else
167241 {
168242 Uri faux = new ( FallbackBaseUri , uri ) ;
169243 host = default ;
170244 path = PathString . FromUriComponent ( faux ) ;
171245 queryString = QueryString . FromUriComponent ( faux ) ;
172- query = GetQueryComponent ( queryString ) ;
173246 }
174247 }
175248
176- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
177- private static QueryCollection GetQueryComponent ( QueryString query )
178- => new ( QueryHelpers . ParseQuery ( query . Value ) ) ;
249+ private HttpContext ToHttpContext ( HostString host , PathString path , QueryString queryString , QueryCollection query )
250+ {
251+ DefaultHttpContext context = new ( ) { RequestServices = this . serviceProvider } ;
252+ HttpRequest request = context . Request ;
253+ request . Method = HttpMethods . Get ;
254+ request . Host = host ;
255+ request . Path = path ;
256+ request . QueryString = queryString ;
257+ request . Query = query ;
258+
259+ return context ;
260+ }
179261 }
180262}
0 commit comments