Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Update static files sample to use AuthorizationMiddleware #65

Closed
wants to merge 3 commits into from

Conversation

JamesNK
Copy link
Member

@JamesNK JamesNK commented Nov 2, 2018

@JamesNK
Copy link
Member Author

JamesNK commented Nov 2, 2018

Note: ReturnUrl is messed up because of aspnet/Security#1730 😢

@Tratcher
Copy link
Member

Tratcher commented Nov 2, 2018

Luckily aspnet/Security#1730 is getting fixed in aspnet/Security#1887

@@ -98,12 +110,45 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAuthori

app.Map("/MapAuthenticatedFiles", branch =>
{
MapAuthenticatedFiles(branch, files);
// Blanket authorization, any authenticated user is allowed access to these resources.
branch.UseAuthorization(new AuthorizationOptions
Copy link
Member

@HaoK HaoK Nov 3, 2018

Choose a reason for hiding this comment

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

I don't really like creating a stand alone AuthorizationOptions which is detached from the normal AuthorizationOptions that someone might have configured.

What if this was branch.UseAuthorization("AuthenticatedFilesPolicy") which has the middleware automatically combine the policy specified before doing Authorize, but doesn't use a decoupled options instance.

So each instance of the middleware sorta behaves like an Authorize attribute, where you can add additional requirements.

@JamesNK
Copy link
Member Author

JamesNK commented Nov 4, 2018

🆙 📅

I refactored the sample to rely more on setting an endpoint with metadata and less on branching and having different AuthZ policies on different branches.

There is a single point of authorization and the sample longer needs a UseAuthorization(options) overload or RequiredPolicy.

I think this is better way of doing it.

@HaoK
Copy link
Member

HaoK commented Nov 5, 2018

Yeah this looks much better!


var endpoint = new Endpoint(
c => Task.CompletedTask,
new EndpointMetadataCollection(metadata),
Copy link
Member

Choose a reason for hiding this comment

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

Condense: new EndpointMetadataCollection(fileSystemInfo, new AuthorizeAttribute(policy)),

metadata.Add(new AuthorizeAttribute(policy));

var endpoint = new Endpoint(
c => Task.CompletedTask,
Copy link
Member

Choose a reason for hiding this comment

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

null? Otherwise this breaks 404.
It's a bit awkward to use Endpoints for auth without using them to actually handle the request.

Copy link
Member Author

Choose a reason for hiding this comment

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

It doesn't break 404 because an endpoint is only being created for a file, and files are always being return by UseFileSystem middleware.

The endpoint request delegate is never being called, but if it was then returning a null would NRE when it is awaited.

Copy link
Member

Choose a reason for hiding this comment

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

It should never be awaited. If it matches a file then this would never get run. If it doesn't match a file then calling this would be wrong.
https://github.com/aspnet/HttpAbstractions/pull/1059/files#diff-3376870948b08e8c913217b7dab1bdcbR86

Copy link
Member

Choose a reason for hiding this comment

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

Better yet, since we don't expect it to get called it should throw InvalidOperationException.

return null;
}

private class EndpointFeature : IEndpointFeature
Copy link
Member

Choose a reason for hiding this comment

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

Callers shouldn't have to create this dummy type. See the comments about Get/SetEndpoint() extensions.

new EndpointMetadataCollection(metadata),
context.Request.Path);

context.Features.Set<IEndpointFeature>(new EndpointFeature(endpoint));
Copy link
Member

Choose a reason for hiding this comment

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

This warrants a SetEndpoint extension method that creates the dummy feature for you internally. We do this for other features already. And while you're at it add the matching GetEndpoint extension.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good suggestion! How would SetEndpoint work if there is already an IEndpointFeature set? Would it update the Endpoint property and leave the same feature, or would it always set a new feature with endpoint.

I ask because the implementation that routing sets implements multiple feature interfaces, e.g. IEndpointFeature and IRouteValuesFeature and IRouterFeature (for legacy).

Copy link
Member

@Tratcher Tratcher Nov 6, 2018

Choose a reason for hiding this comment

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

I would not expect it to replace the feature implementation, only the endpoint. The features need to function independently.

@@ -96,16 +109,27 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAuthori

var files = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "PrivateFiles"));

app.Map("/MapAuthenticatedFiles", branch =>
app.Use(async (context, next) =>
Copy link
Member

Choose a reason for hiding this comment

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

This two stage process seems unnecessary.

app.Map("/MapImperativeFiles", branch => {
  branch.Use((context, next) => { SetFileEndpoint(context, files, "files"); return next(); })
  branch.UseAuthorization();
  SetupFileServer(branch, files);
});
app.Map("/MapAuthenticatedFiles", branch => {
  branch.Use((context, next) => { SetFileEndpoint(context, files, null); return next(); })
  branch.UseAuthorization();
  SetupFileServer(branch, files);
});

Which could be consolidated to:

void MapFileAuth(IApplicationBuilder app, string path, IFileProvider files, string policy)
{
app.Map(path, branch => {
  branch.Use((context, next) => { SetFileEndpoint(context, files, policy); return next(); })
  branch.UseAuthorization();
  SetupFileServer(branch, files);
});
}

MapFileAuth(app, "/MapImperativeFiles", files, "files");
MapFileAuth(app, "/MapAuthenticatedFiles", files, null);

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmmmm, I prefer the current way it is working. There is a single point of authorization for all requests.

Do you not the process of calculating and setting an endpoint for a file? After I updated this sample I raised an issue about making this more of a first class citizen here - https://github.com/aspnet/Routing/issues/903

I'd like it to look something like:

// For files sitting in wwwroot
app.UseStaticFiles();

app.UseEndpointRouting(routes =>
{
    // MVC
    routes.MapController(name: "default", template: "{controller=Home}/{action=Index}/{id?}");

    // Static file endpoints for files in non-standard locations
    routes.MapStaticFiles("/MapImperativeFiles").RequireAuthorization("files");
    routes.MapStaticFiles("/MapAuthenticatedFiles").RequireAuthorization();
});

app.UseAuthorization();

I don't think this sample would be able to become quite this clean. The branching, and different URLs pointed at the same files makes things messy. Have you considered not having branching in this sample? I think it would make it more real world if there was a set of directories that required auth, and another set that required auth from a specific person.

// @rynowak

Copy link
Member

@Tratcher Tratcher Nov 6, 2018

Choose a reason for hiding this comment

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

Having a single UseAuthorization per branch makes sense, but don't artificially bend the pipeline back on itself just to reduce the calls to UseAuthorization. You're effectively duplicating the Map code before and after UseAuthorization when it's a lot cleaner not to.

Copy link
Member

Choose a reason for hiding this comment

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

I do like where aspnet/Routing#903 is going, but we're not there yet.

Copy link
Member

Choose a reason for hiding this comment

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

I agree the overlapping file structure is not realistic, but the branching is.

@natemcmaster
Copy link
Contributor

This repo is going to be archived. AuthSamples are now part of aspnet/AspNetCore.

cref dotnet/aspnetcore#4088

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants