Skip to content

Commit 879f990

Browse files
Add overloads and cleanup
1 parent 06f99a4 commit 879f990

File tree

4 files changed

+196
-39
lines changed

4 files changed

+196
-39
lines changed

src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCache.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4+
using System;
45
using System.Collections.Generic;
6+
using System.Globalization;
57
using System.IO;
8+
using System.Threading;
69
using System.Threading.Tasks;
710
using Amazon.S3;
811
using Amazon.S3.Model;
@@ -119,5 +122,53 @@ private static async Task<PutBucketResponse> CreateIfNotExistsAsync(
119122

120123
return null;
121124
}
125+
126+
/// <summary>
127+
/// <see href="https://github.com/aspnet/AspNetIdentity/blob/b7826741279450c58b230ece98bd04b4815beabf/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs"/>
128+
/// </summary>
129+
internal static class AsyncHelper
130+
{
131+
private static readonly TaskFactory TaskFactory
132+
= new(
133+
CancellationToken.None,
134+
TaskCreationOptions.None,
135+
TaskContinuationOptions.None,
136+
TaskScheduler.Default);
137+
138+
/// <summary>
139+
/// Executes an async <see cref="Task"/> method synchronously.
140+
/// </summary>
141+
/// <param name="task">The task to excecute.</param>
142+
public static void RunSync(Func<Task> task)
143+
{
144+
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
145+
CultureInfo culture = CultureInfo.CurrentCulture;
146+
TaskFactory.StartNew(() =>
147+
{
148+
Thread.CurrentThread.CurrentCulture = culture;
149+
Thread.CurrentThread.CurrentUICulture = cultureUi;
150+
return task();
151+
}).Unwrap().GetAwaiter().GetResult();
152+
}
153+
154+
/// <summary>
155+
/// Executes an async <see cref="Task{TResult}"/> method which has
156+
/// a <paramref name="task"/> return type synchronously.
157+
/// </summary>
158+
/// <typeparam name="TResult">The type of result to return.</typeparam>
159+
/// <param name="task">The task to excecute.</param>
160+
/// <returns>The <typeparamref name="TResult"/>.</returns>
161+
public static TResult RunSync<TResult>(Func<Task<TResult>> task)
162+
{
163+
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
164+
CultureInfo culture = CultureInfo.CurrentCulture;
165+
return TaskFactory.StartNew(() =>
166+
{
167+
Thread.CurrentThread.CurrentCulture = culture;
168+
Thread.CurrentThread.CurrentUICulture = cultureUi;
169+
return task();
170+
}).Unwrap().GetAwaiter().GetResult();
171+
}
172+
}
122173
}
123174
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using SixLabors.ImageSharp.Web.Commands;
5+
6+
namespace SixLabors.ImageSharp.Web
7+
{
8+
/// <summary>
9+
/// Provides enumeration for handling <see cref="CommandCollection"/> instances
10+
/// when processing a request.
11+
/// </summary>
12+
public enum CommandHandling
13+
{
14+
/// <summary>
15+
/// The command collection will be stripped of any unknown commands.
16+
/// </summary>
17+
Sanitize,
18+
19+
/// <summary>
20+
/// The command collection will be processed unaltered.
21+
/// </summary>
22+
None
23+
}
24+
}

src/ImageSharp.Web/ImageSharpRequestAuthorizationUtilities.cs

Lines changed: 120 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6-
using System.Runtime.CompilerServices;
6+
using System.Globalization;
77
using System.Threading.Tasks;
88
using 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
}

src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ private static readonly ConcurrentTLruCache<string, string> HMACTokenLru
129129
/// <param name="cache">An <see cref="IImageCache"/> instance used for caching images.</param>
130130
/// <param name="cacheKey">An <see cref="ICacheKey"/> instance used for creating cache keys.</param>
131131
/// <param name="cacheHash">An <see cref="ICacheHash"/>instance used for calculating cached file names.</param>
132-
/// <param name="commandParser">The command parser</param>
132+
/// <param name="commandParser">The command parser.</param>
133133
/// <param name="formatUtilities">Contains various format helper methods based on the current configuration.</param>
134134
/// <param name="asyncKeyLock">The async key lock</param>
135135
public ImageSharpMiddleware(

0 commit comments

Comments
 (0)