WebOptimizer icon indicating copy to clipboard operation
WebOptimizer copied to clipboard

System.UnauthorizedAccessException: Access to the path 'C:\inetpub\wwwroot\mysite\myapp\obj\WebOptimizerCache' is denied.

Open VictorioBerra opened this issue 3 years ago • 8 comments

This happens during IIS cold starts.

  • dotnet core 5
  • IIS
  • <PackageReference Include="LigerShark.WebOptimizer.Core" Version="3.0.304" />
  • <PackageReference Include="LigerShark.WebOptimizer.Sass" Version="3.0.43-beta" />

Startup - WebOpt Configuration

return services.AddWebOptimizer(pipeline =>
        {
            // Main css bundle (pre-minified files)
            pipeline
                .AddCssBundle(
                    "/css/bundle.min.css",
                    "/lib/datatables/css/dataTables.bootstrap4.min.css",
                    "/lib/datatables.net-buttons-bs4/buttons.bootstrap4.css");

            // Main js bundle (pre-minified files)
            pipeline
                .AddJavaScriptBundle(
                    "/js/bundle.min.js",
                    "/lib/jszip/jszip.min.js", // Dependency for Datatables excel export Button
                    "/lib/pdfmake/pdfmake.min.js", // Dependency for Datatables pdf export Button
                    "/lib/pdfmake/vfs_fonts.min.js", // Dependency for Datatables pdf export Button
                    "/lib/jquery/jquery.min.js",
                    "/lib/jquery-validation/dist/jquery.validate.min.js",
                    "/lib/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.js",
                    "/lib/bootstrap/dist/js/bootstrap.min.js",
                    "/lib/datatables/js/jquery.dataTables.min.js",
                    "/lib/datatables-buttons/js/dataTables.buttons.min.js",
                    "/lib/datatables-buttons/js/buttons.html5.min.js",
                    "/lib/datatables-buttons/js/buttons.print.min.js",
                    "/lib/datatables.net-buttons-bs4/buttons.bootstrap4.min.js",
                    "/lib/datatables/js/dataTables.bootstrap4.min.js");

            // Nested bundles would be great.
            // https://github.com/ligershark/WebOptimizer/issues/159
            // IE bundle up all the pre-minified stuff above, and then minify some additional files with WebOptimizer and concatenate it all together.
            pipeline
                .MinifyJsFiles("/js/site.js");
            pipeline
            .MinifyCssFiles("/css/site.css");

            // Enable referencing .scss files directly in the browser (http://domain/a.scss)
            pipeline.CompileScssFiles();

            // Take our custom Bootstrap SCSS and compile+minify it.
            pipeline
            .AddScssBundle("/css/bootstrap.custom.min.css", "css/bootstrap.custom.scss")
            .MinifyCss();
        });

Startup - Configure

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseSecurityHeaders();

    app.UseWebOptimizer();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
    }

    app.UseStaticFiles();

    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseCustomSerilogRequestLogging();

    app.UseMiniProfiler();

    app.UseEndpoints(endpoints =>
    {
        endpoints
            .MapRazorPages()
            .RequireAuthorization();
    });
}

Error

Exception: System.UnauthorizedAccessException: Access to the path 'C:\inetpub\wwwroot\mysite\myapp\obj\WebOptimizerCache' is denied.
   at System.IO.FileSystem.CreateDirectory(String fullPath, Byte[] securityDescriptor)
   at System.IO.Directory.CreateDirectory(String path)
   at WebOptimizer.AssetResponseStore.AddAsync(String bucket, String cachekey, AssetResponse assetResponse)
   at WebOptimizer.AssetBuilder.BuildAsync(IAsset asset, HttpContext context, IWebOptimizerOptions options)
   at WebOptimizer.AssetMiddleware.HandleAssetAsync(HttpContext context, IAsset asset, WebOptimizerOptions options)
   at NetEscapades.AspNetCore.SecurityHeaders.SecurityHeadersMiddleware.Invoke(HttpContext context) in C:\projects\netescapades-aspnetcore-securityheaders\src\NetEscapades.AspNetCore.SecurityHeaders\SecurityHeadersMiddleware.cs:line 68
   at Microsoft.AspNetCore.Builder.Extensions.UsePathBaseMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()

VictorioBerra avatar May 04 '21 16:05 VictorioBerra

It appears that setting enableDiskCache to false fixes the issue. Does it make sense to do that?

VictorioBerra avatar May 04 '21 20:05 VictorioBerra

Maybe better is to set cacheDirectory to directory with read/write permitions for app.

matyasbach avatar Oct 06 '21 18:10 matyasbach

I think the default for cacheDirectory is invalid. It points into the web app's content root (If later fixed, I've found it in this commit )

A production web application should not have write access on its own directory, that would open security concerns. To add write access only to the default <contentRoot>/obj folder would need the deployment set up properly. That's unnecessary extra work. Why not just use the web application's temp folder?

nvirth avatar Nov 23 '21 10:11 nvirth

@nvirth Do web apps have temp folders they can write to by default in IIS?

VictorioBerra avatar Nov 23 '21 15:11 VictorioBerra

I think yes. But I'm not sure. But then I can't remember setting up any folder credentials for HttpPostedFileBase to work in the old ASP.NET MVC. That saves the uploaded files into a temporary folder - maybe it's the Temporary ASP.NET Files folder somewhere, don't really know that.

Anyway, I just fixed the issue in my project with a custom MyWebOptimizerConfig : IConfigureOptions<WebOptimizerOptions> implementation. In this I set CacheDirectory to Path.GetTempPath(). I applied it this way:

        services.AddWebOptimizer(...);
        services.Remove(services.Single(s => s.ServiceType == typeof(IConfigureOptions<WebOptimizerOptions>)));
        services.AddTransient<IConfigureOptions<WebOptimizerOptions>, MyWebOptimizerConfig>();

nvirth avatar Nov 23 '21 20:11 nvirth

@VictorioBerra Path.GetTempPath() does not always work. On one of our new test servers, we've seen this log now: System.UnauthorizedAccessException: Access to the path 'C:\Windows\TEMP' is denied.

nvirth avatar Jan 13 '22 15:01 nvirth