New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need a way to handle authorization for static files #2457

Open
aspnet-hello opened this Issue Jan 1, 2018 · 11 comments

Comments

Projects
None yet
8 participants
@aspnet-hello
Copy link

aspnet-hello commented Jan 1, 2018

From @danroth27 on Thursday, September 24, 2015 11:18:42 AM

In ASP.NET 4.6 you can use the Authorization module to handle authorization for static file requests. We need some what to handle authorization for static files in ASP.NET 5.

@blowdart

Copied from original issue: aspnet/StaticFiles#70

@aspnet-hello

This comment has been minimized.

Copy link

aspnet-hello commented Jan 1, 2018

From @blowdart on Thursday, September 24, 2015 11:21:47 AM

My feeling here is we don't want yet another middleware for this. The Static File middleware should detect the authorization service and enable configuration of policies by path, file type, regex (kidding - NO REGEX) or whatever other filtering mechanism they wish to implement.

Pushing responsibility off to another middleware, which then becomes order dependent is a path of failure and undiscoverable for most users.

@aspnet-hello

This comment has been minimized.

Copy link

aspnet-hello commented Jan 1, 2018

From @longday24 on Saturday, April 9, 2016 6:51:53 AM

What if UseStaticFiles() excepted optional string param of a named authorizationPolicy when passing requestpath?

public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(); services.AddAuthorization(options => { options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser()); }); }

usage would be something like .UseStaticFiles("requestPath", "Authenticated");

@aspnet-hello

This comment has been minimized.

Copy link

aspnet-hello commented Jan 1, 2018

From @kyse on Thursday, June 29, 2017 1:59:57 PM

Just to push this along figured I'd throw this out there.

Create a StaticFileAuthorizationRequirement file that inherits IAuthorizationRequirement for developers to build AuthorizationHandleres against. Not essential, we can realistically pass in any requirement in updated options, there's not really a need for injecting anything specific to this library to the requirement except for is it authorizing a directory browser or static file request, which can just be checked against the context.Resource parameter in the handler.

Update SharedOptions and SharedOptionsBase to provide IEnumerable AuthenticationSchemes, IEnumerable AuthorizationRequirements, and bool AllowAnonymous properties.

In Helpers.cs:

        internal static bool IsAuthenticated(this HttpContext context, SharedOptionsBase options)
        {
            if (options.AllowAnonymous)
                return true;

            var authSchemes = options.AuthenticationSchemes ?? context.Authentication.GetAuthenticationSchemes().Select(desc => desc.AuthenticationScheme).ToArray();

            foreach (var authScheme in authSchemes)
            {
                var cp = context.Authentication.AuthenticateAsync(authScheme).Result;
                if (cp == null) continue;
                context.User = cp;
                break;
            }
            return (context.User != null && context.User.Identity.IsAuthenticated);
        }

        internal static bool IsAuthorized(this HttpContext context, ILibrary library, SharedOptionsBase options)
        {
            return options.AllowAnonymous ||
                   !options.AuthorizationRequirements.Any() ||
                   context.RequestServices.GetService<IAuthorizationService>().AuthorizeAsync(context.User, library, options.AuthorizationRequirements).Result;
        }

In the directory browser and static file middleware, update body to include calls to authenticate and authorize.
DirectoryBrowserMiddleware:

            if (!context.IsAuthenticated(_options))
            {
                context.Response.StatusCode = 401;
                return Constants.CompletedTask;
            }

            if (!context.IsAuthorized(library, _options))
            {
                context.Response.StatusCode = 403;
                return Constants.CompletedTask;
            }

StaticFileMiddleware:

            else if (!fileContext.LookupFileInfo())
            {
                _logger.LogFileNotFound(fileContext.SubPath);
            }
            else
            {
                if (!context.IsAuthenticated(_options))
                {
                    _logger.LogNotAuthenticated(fileContext.Library.Name, fileContext.SubPath);
                    return fileContext.SendStatusAsync(Constants.Status401NotAuthenticated);
                }
                if (!context.IsAuthorized(fileContext.Library, _options))
                {
                    _logger.LogNotAuthorized(fileContext.Library.Name, fileContext.SubPath);
                    return fileContext.SendStatusAsync(Constants.Status403NotAuthorized);
                }

Update logger to expose authenticated and authorized logging templates, as well as Constants to include the two additional status codes.

StaticFileAuthorizationHandler : AuthorizationHandler can be defined to parse against the ClaimsPrincipal or even an injected DB context (however you want to setup your handler) for authorizing the static file request.

I further updated my customization to build this out based on entries my DB for ILibrary, thus I can define a LibraryServer with an endpoint of library, and it parses libraries and generates PhysicalFileProvider and serves the Path for the libraries exposed based on authentication and authorization. Since this library treats each instance as a single path there's on need for that and would probably be best for this repo to instead utilize the authorization handler to treat the resource as the type of request being authorized, ie allow viewing the directory browser, or allow grabbing the static file.

Then you can just allow the developer to add in the auth requirements as desired:

            app.UseLibraryServer(new LibraryServerOptions
            {
                RequestPath = new PathString("/Library"),
                PathProvider = dbContextOrRepositoryOrServiceInheritingILibraryPathProvider,
                AuthenticationSchemes = new List<string> { CookieAuthenticationDefaults.AuthenticationScheme },
                AuthorizationRequirements = new List<IAuthorizationRequirement> { new LibraryAuthorizationRequirement() },
                LibraryFileOptions =
                {
                    ServeUnknownFileTypes = true
                }
            });

Obviously in that example I'm also providing a PathProvider for my db library entities that is out of scope of this post. But you could pass AllowAnonymous = true, or pass in desired authentication schemes to target or against any defined, and any specific requirements. Probably best to further expand it to allow just providing a policy name instead.

I could probably put together a pull request if you like this direction without the dynamic sub path options. Biggest question would be is it worth while to provide a base requirement class or just let the developer define their own.

@lumogox

This comment has been minimized.

Copy link

lumogox commented Feb 6, 2018

Any updates on this? @aspnet-hello

@muratg muratg removed this from the 2.1.0 milestone Mar 3, 2018

@muratg

This comment has been minimized.

Copy link
Member

muratg commented Mar 3, 2018

@blowdart What's your current thoughts on this?

@blowdart

This comment has been minimized.

Copy link
Member

blowdart commented Mar 3, 2018

How about the same way you handled razor pages, with paths in the config?

@muratg muratg added this to the 2.2.0 milestone Mar 5, 2018

@muratg

This comment has been minimized.

Copy link
Member

muratg commented Mar 5, 2018

Tentatively putting into the next milestone.

@kyse

This comment has been minimized.

Copy link

kyse commented Mar 5, 2018

FYI, in the mean time. You could make use of this to achieve auth on static files.
https://github.com/kyse/Kyse.AspNetCore.StaticLibrary

@Tratcher

This comment has been minimized.

Copy link
Member

Tratcher commented Apr 20, 2018

Here's a sample showing how to do AuthN and AuthZ for static files as a middleware on a pipeline branch:
https://github.com/aspnet/AuthSamples/blob/c7bb6607307072f6e098d0e4a2c9fa7e6d017916/samples/StaticFilesAuth/Startup.cs#L120-L134

This isn't ideal, we should still figure out how to integrate this as a first class citizen.

I also tried writing a sample using MVC's Authorize features, but MVC does not provide good handling for serving static files, directory browsing, default files, etc... See the discussion at aspnet/AuthSamples#37.

@muratg muratg modified the milestones: 2.2.0-preview1, 3.0.0 Aug 23, 2018

natemcmaster pushed a commit that referenced this issue Nov 14, 2018

@muratg muratg modified the milestones: 3.0.0-preview1, 3.0.0-preview2 Nov 30, 2018

@muratg muratg modified the milestones: 3.0.0-preview2, 3.0.0 Jan 10, 2019

@shirhatti

This comment has been minimized.

Copy link
Member

shirhatti commented Jan 11, 2019

@Tratcher In 3.0, should we rewrite static files as an end-ware? That should solve this problem

@Tratcher

This comment has been minimized.

Copy link
Member

Tratcher commented Jan 11, 2019

@shirhatti it's worth a try. You'd still need a way to mark files as requiring authorization policies though.

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