catch all file extension controller action together with static files - asp.net-core

I have a set of files in the wwwroot folder. With webpack I generate js bundles with js.map files. I want to put security on the *.map files to only allow certain people to have access to it.
The documentation of ASP.NET core static files concludes that all files in wwwroot have no security. If security is needed advice is given to make an MVC controller and to put security on the action method.
I want to have the static files in wwwroot with one catch all *.js.map URLs which performs security and gives a notfound. How do I define this action method.
I don't want to put the map files in a different folder and to serve them differently.

Define a route like this with a catchAll template and a constraint:
app.UseMvc(routes =>
{
// https://haacked.com/archive/2008/07/14/make-routing-ignore-requests-for-a-file-extension.aspx/
routes.MapRoute(
name: "MapFiles",
template: "{*relativeFileUrl}",
defaults: new { controller = "FileSystem", action = "AccessDevFile" },
constraints: new { relativeFileUrl = #".*\.js.map" }
);
});
The action method looks like this. If no access allowed we just return NotFound()
public IActionResult AccessDevFile(string relativeFileUrl)
{
bool hasAccess = ... do your check ...
if(hasAccess)
{
return base.PhysicalFile(_hostingEnvironment.WebRootPath + "\\" + relativeFileUrl.Replace("/", #"\"), "application/json");
}
return NotFound();
}
Also make sure to have the UseMvc call before the UserStaticFiles call otherwise the action method can never be reached.

Related

Check if request is made to Razor Page

How can I check within middleware code if current request is made to Razor Page not to any other resource (static file, or API)?
All my APIs are located within api folder, so if (!context.Request.Path.StartsWithSegments("/api")) {} filters out APIs, but that will not work for static content as these files and libraries are placed within number of folders what results in number of URL segments.
Could not find any relevant property in context.
First step - place the middleware after app.UseRouting(). Any request for a static file will never reach your middleware because the static files middleware will short circuit the request. Also, after this point, the routing middleware will have selected the endpoint and populated the endpoint metadata. Then you can test the endpoint metadata collection to see if it includes PageRouteMetaData, which tells you that this is a Razor page route:
app.Use((context, next) => {
var endpoint = context.GetEndpoint();
if (endpoint != null)
{
foreach(var md in endpoint.Metadata)
{
if( md is PageRouteMetadata)
{
// this is a page route
}
}
}
return next(context);
});

How to grant/limit access on Images sub folders for each Customer?

I have the following structure inside my wwwroot/images:
wwwroot
-------Images
-------------Customers
-----------------------Each costumers has his own folder name.
Each folder has an image which only the customer might have access.
The problem is: If some user types the address of other user's image, it will open.
I'm trying to restrict the access of each folder using the table Company from the data base.
I could do it on MVC 5 using location path on web config.
But how could I do it in appsettings.json on .NET Core?
Thanks guys!
OBS: If you have any other approach for this, will be welcome :D
The Static File Middleware doesn't provide authorization checks. Any files served by it, including those under wwwroot, are publicly accessible. To serve files based on authorization, you could refer Static file authorization.
For another option, you may consider implement a custom middleware check the identity like
app.Map("/specificpath", subApp => {
subApp.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
else if(context.Request.Path.StartsWithSegments("/specificpath/User1") && context.User.Identity.Name != "User1")
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
});
});
app.UseStaticFiles();

.NET Core serving HTML file outside wwwroot can't load JS files

I am experimenting with different ways to secure an Angular CLI app with .NET Core Authorization.
To make it as secure as possible, I would like to keep all of the Angular CLI output files from being publicly available and keep them in the default "dist" folder preconfigured by the CLI.
I can load the index.html from an authorized controller by returning a PhysicalFileResult...
public IActionResult Index()
{
return PhysicalFile(Path.Combine(Directory.GetCurrentDirectory(), "dist", "index.html"),"text/HTML");
}
But I get 404s on all of the bundle.js files when the page loads.
Is it possible to serve the app this way without involving the static file middleware or making the files publicly available (preferably without having to manually change the src for each bundled js file in index.html)?
Take a look at this article from the asp.net core docs (excerpt included below): https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files#static-file-authorization
Just place your authorization middleware before the static one.
// has to be first so user gets authenticated before the static middleware is called
app.UseIdentity();
app.Use(async (context, next) =>
{
// for pathes which begin with "app" check if user is logged in
if(context.Request.Path.StartsWith("app") && httpContext.User==null)
{
// return "Unauthorized"
context.Response.StatusCode = 401;
return;
}
// If user is logged in, call next middleware
await next.Invoke();
});
app.UseStaticFiles();

Filter for static file middleware

Is there a way to intercept the request before it got serve, so I can edit a picture or create dynamic pdf on demand? I tried using MapArea and redirect the request to a controller, but when I use staticfiles middleware, it catch the request, and my controller wont handle the request.
If your static files does not exist and you want generate them on-thy-fly - it's better to create your own middelware and register it before UseStaticFiles.
If files exist, but you want "slightly" modify response (for different users for example) - you may use OnPrepareResponse handler in static file options:
var staticFileOptions = new StaticFileOptions
{
OnPrepareResponse = (context) =>
{
var fn = context.File.Name.ToLowerInvariant();
if (fn.EndsWith(".pdf"))
{
SomeService.LogPdfDownload(context.Context.Response);
}
else
{
context.Context.Response.Headers.Add("Cache-Control", "public, max-age=15552000"); // 180 days
}
}
};
app.UseStaticFiles(staticFileOptions);
From docs: OnPrepareResponse is called after the status code and headers have been set, but before the body has been written
Is there a way to intercept the request before it got serve
Yes. You can write your own middleware and add it to IApplicationBuilder before you call UseStaticFiles. See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware#ordering
See also https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing. You may also be able to solve this problem by writing routes instead of middleware.

MVC 4 mapping files in directory to controller action to ensure authorized access to documents

If I have a file in a directory on my website:
~/documents/file1.pdf
How can I force requests to the following url to go through a controller:
www.mydomain.com/documents/file1.pdf
The controller would then look something like
[Authorize]
public DocumentsController : Controller
{
public FileResult Index(string fileName)
{
//Check user has permission to view file
return File(...);
}
}
I got round this by putting the documents directory in the App_Data folder so it's not accessible to the public and the using the document's ID from the database to retrieve the relevant information e.g.
/documents/21
I think I prefer this approach anyway to my initial plan because it abstracts the identity of the path and document title far more thoroughly as the controller path can be anything and doesn't have to map to a physical directory path.
This is not difficult, so long as you can make some assumptions.
First, you create your action method as you propose:
[Authorize]
public DocumentsController : Controller
{
public FileResult Index(string fileName)
{
//Check user has permission to view file
return File(...);
}
}
Next, you create a route:
routes.MapRoute(
name: "Documents",
url: "documents/{fileName}",
defaults: new { controller = "Documents", action = "Index" }
You may be required to relax the url validation requirements by adding the following to web.config (this only works in IIS7-8 though as far as I know, IIS6 would require an http module I think):
<httpRuntime relaxedUrlToFileSystemMapping="true" />