Skip to content

Conversation

@JimBobSquarePants
Copy link
Member

@JimBobSquarePants JimBobSquarePants commented Jul 7, 2022

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Adds a new type ImageSharpRequestAuthorizationUtilities which completes the HMAC story, allowing developers to create tokens directly using the functions defined in the middleware options.

@codecov
Copy link

codecov bot commented Jul 7, 2022

Codecov Report

Merging #281 (eadeef6) into main (0a62c3b) will increase coverage by 0%.
The diff coverage is 88%.

@@         Coverage Diff          @@
##           main   #281    +/-   ##
====================================
  Coverage    84%    85%            
====================================
  Files        75     76     +1     
  Lines      2047   2169   +122     
  Branches    299    303     +4     
====================================
+ Hits       1739   1857   +118     
- Misses      223    227     +4     
  Partials     85     85            
Flag Coverage Δ
unittests 85% <88%> (+<1%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/ImageSharp.Web/PathUtilities.cs 100% <ø> (+20%) ⬆️
...arp.Web.Providers.AWS/Caching/AWSS3StorageCache.cs 78% <60%> (-10%) ⬇️
...Sharp.Web/Commands/QueryCollectionRequestParser.cs 71% <66%> (ø)
...arp.Web/ImageSharpRequestAuthorizationUtilities.cs 90% <90%> (ø)
.../ImageSharp.Web/Middleware/ImageSharpMiddleware.cs 80% <93%> (-1%) ⬇️
src/ImageSharp.Web/AsyncHelper.cs 100% <100%> (ø)
src/ImageSharp.Web/Caching/UriAbsoluteCacheKey.cs 100% <100%> (ø)
...p.Web/Caching/UriAbsoluteLowerInvariantCacheKey.cs 100% <100%> (ø)
src/ImageSharp.Web/CaseHandlingUriBuilder.cs 88% <100%> (+22%) ⬆️
...Commands/PresetOnlyQueryCollectionRequestParser.cs 100% <100%> (ø)
... and 4 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0a62c3b...eadeef6. Read the comment docs.

Copy link
Contributor

@ronaldbarendse ronaldbarendse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be a great addition and will make (correct) use of the HMAC image processing protection a lot easier! I've added some comments/suggestions and would love to test this when it's ready for review 👍🏻

@JimBobSquarePants JimBobSquarePants changed the title WIP Add Authorization Utilitites Add Authorization Utilitites Jul 9, 2022
@JimBobSquarePants JimBobSquarePants marked this pull request as ready for review July 9, 2022 13:44
@JimBobSquarePants JimBobSquarePants requested a review from a team July 9, 2022 13:44
@JimBobSquarePants
Copy link
Member Author

@ronaldbarendse Good feedback, thanks! I've pushed all the fixes.

Copy link
Contributor

@ronaldbarendse ronaldbarendse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've successfully tested this PR together with a massively simplified version of umbraco/Umbraco-CMS#12376 (replacing the custom ImageSharpImageUrlTokenGenerator abstraction/implementation with ImageSharpRequestAuthorizationUtilities) 🙌🏻

I've only added some final comments about backwards compatibility and naming, but looks good to me otherwise!

FormatUtilities formatUtilities,
AsyncKeyReaderWriterLock<string> asyncKeyLock)
AsyncKeyReaderWriterLock<string> asyncKeyLock,
ImageSharpRequestAuthorizationUtilities requestAuthorizationUtilities)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this parameter is a (binary) breaking change which can be avoided by re-adding the previous constructor as obsolete and manually constructing the ImageSharpRequestAuthorizationUtilities:

[Obsolete]
public ImageSharpMiddleware(
    RequestDelegate next,
    IOptions<ImageSharpMiddlewareOptions> options,
    ILoggerFactory loggerFactory,
    IRequestParser requestParser,
    IEnumerable<IImageProvider> resolvers,
    IEnumerable<IImageWebProcessor> processors,
    IImageCache cache,
    ICacheKey cacheKey,
    ICacheHash cacheHash,
    CommandParser commandParser,
    FormatUtilities formatUtilities,
    AsyncKeyReaderWriterLock<string> asyncKeyLock)
    : this(
        next,
        options,
        loggerFactory,
        requestParser,
        resolvers,
        processors,
        cache,
        cacheKey,
        cacheHash,
        commandParser,
        formatUtilities,
        asyncKeyLock,
        new ImageSharpRequestAuthorizationUtilities(options, requestParser, processors, commandParser, null))
{
}

This does require ImageSharpRequestAuthorizationUtilities to allow a null value for IServiceProvider, but that shouldn't be an issue, as it isn't required/used by the methods this middleware calls into anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it’s binary breaking but to upgrade you’re gonna have to recompile anyway. Obsolete isn’t actually a workaround due to consumer rulesets which can be violated using the attribute.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While looking into this a bit more, I noticed ImageSharpRequestAuthorizationUtilities is currently only really required to strip unknown commands, as ComputeHMACAsync(ImageCommandContext) just delegates to this.options.OnComputeHMACAsync(ImageCommandContext, byte[]). I also think that stripping unknown commands isn't a request authorization concern, so it's probably best to remove this dependency on ImageSharpRequestAuthorizationUtilities altogether...

this.logger = loggerFactory.CreateLogger<ImageSharpMiddleware>();
this.formatUtilities = formatUtilities;
this.asyncKeyLock = asyncKeyLock;
this.authorizationUtilities = requestAuthorizationUtilities;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the field also be named requestAuthorizationUtilities?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, that’s just ocd

/// <summary>
/// Contains various helper methods for authorizing image requests.
/// </summary>
public sealed class ImageSharpRequestAuthorizationUtilities
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be renamed to RequestAuthorizationUtilities, similar to FormatUtilities, HMACUtilities, etc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make it more explicit. It’s for ImageSharp requests only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but it's already in the SixLabors.ImageSharp.Web namespace and consumers can otherwise alias the class name if it would conflict with something else:

using ImageSharpRequestAuthorizationUtilities = SixLabors.ImageSharp.Web.RequestAuthorizationUtilities;

And after mentioning that, I acknowledge that you can also use that to do the reverse 😓 So it's more to align with the naming used by all the other utility classes...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming is about about consumption intent. All the other types are designed to be used conceptionally within other ImageSharp.Web type implementations (processors generally) whereas this type is designed to be consumed from the users code only. It has to be explicit and unfortunately verbose.

@ronaldbarendse
Copy link
Contributor

I've done some further testing, investigation and refactoring to:

  • Rename ImageSharpRequestAuthorizationUtilities to RequestAuthorizationUtilities;
  • Remove the dependency on RequestAuthorizationUtilities in the middleware (as mentioned in this comment);
  • Obsolete HMACUtilities.TokenCommand (as this class isn't responsible for query string/command manipulation and you can now use RequestAuthorizationUtilities.TokenCommand instead);
  • Include minor fixes like using CaseHandlingUriBuilder.Encode() to generate the HMAC token cache key (that ensures the query string/commands are always lowercased), update code styling and do some re-ordering.

You can see all my changes here: js/auth-utils...ronaldbarendse:ImageSharp.Web:js/auth-utils. I can convert this into a PR targeting js/auth-utils if you want to discuss (and easily merge) these, just let me know! 👍🏻

@JimBobSquarePants
Copy link
Member Author

JimBobSquarePants commented Jul 12, 2022

Thanks for having a crack but I think you missed the point of this new type when making some changes.

We absolutely want to be using the new type from the middleware. It ensures the same type process is used on both sides.
We also want to use that type to strip the unknown commands. Whether to expose that method is up for debate.

Moving operations to various types (IEnumerable<IImageWebProcessor>) is never wise, internal or otherwise.

Tests should be cleaned up (I'll copy some of the changes you made across), most definitely but the other random changes (logging, field field order) are unnecessary. Code shouldn't be changed for changes sake. We also never, ever use ObsoleteAttribute in any of the Six Labors libraries. It's a sure fire way to accumulate technical debt that is unmaintainable given the lack of funding and time.

My changes are only binary breaking if someone is directly calling the middleware constructor which they simply should not be doing. I'll be versioning this release 2.1.0 anyway to send a signal that more fundamental changes have taken place.

@JimBobSquarePants
Copy link
Member Author

This is probably good to merge now.

@JimBobSquarePants JimBobSquarePants merged commit 108a615 into main Jul 20, 2022
@JimBobSquarePants JimBobSquarePants deleted the js/auth-utils branch July 20, 2022 02:39
@ronaldbarendse
Copy link
Contributor

@JimBobSquarePants Can this also be released in a 2.0.3 patch version? I'd love to finish the HMAC authorization support in Umbraco (see PR umbraco/Umbraco-CMS#12376) 😇

@JimBobSquarePants
Copy link
Member Author

JimBobSquarePants commented Oct 5, 2022

@ronaldbarendse I'm actually hoping to release a 2.1.0 build very soon. I've been working on a TagHelper which is allowing me to dogfood the utilites a bit better before releasing.

@ronaldbarendse
Copy link
Contributor

ronaldbarendse commented Feb 10, 2023

I see the work on the TagHelper PR has stalled a bit and currently has some unresolved comments, but I would really like to make use of the new authorization utilities. Would it be possible for you to create a 2.0.3 release containing just these changes? 🙏🏻

@JimBobSquarePants
Copy link
Member Author

Let me see what Ican do. No promises though. I've got a lot on just now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants