Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Chris Norwood 131 posts 642 karma points
    Oct 11, 2022 @ 08:20
    Chris Norwood
    0

    Custom front-end Media Controller

    Hello folks,

    We are in the process of upgrading a client from Umbraco 8 to 10.

    As part of the Umbraco 8 site, we have a custom media controller, which checks for a specific document type (and a specific property on the document type) in order to secure media that shouldn't be available if a user is not logged on to the Intranet (so this is front-end security, not back office - any file that's not of this specific document type is returned immediately to avoid issues with media etc).

    In Umbraco 8 this was fairly simple to set up - we had a component which registered the new media controller on the route, and all was fine.

    However, I'm a bit flummoxed as to how to do this in Umbraco 10; I've set up a custom route for /media/index (I've followed this article: https://our.umbraco.com/documentation/Reference/Routing/Custom-Routes/ using the IVirtualPageController approach, but this seems to be more designed for returning a whole page rather than a single item; I've tried making the Controller an IComponent but that won't work because they are scoped as Singletons and we need to access scope-level services).

    I'm aware of the existence of Media Protect - unfortunately that won't work for our use case, because we need to protect the media by a document type and property rather than the location of the item.

    Has anybody managed to successfully implement something like this in Umbraco 9/10? Perhaps there's a better way than overriding the Media controller?

    Edit: I found this post:

    https://our.umbraco.com/forum/using-umbraco-and-getting-started/108979-intercept-all-media-requests

    But I can't inject the services I need to check the media type and property...

    Thanks,

    Chris.

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Oct 11, 2022 @ 17:42
    Huw Reddick
    0

    Hi,

    Yes I do something similar, I created a middleware control which is then registered in startup. I'm not near my computer at the moment but will post my code tomorrow for you to look at.

  • Chris Norwood 131 posts 642 karma points
    Oct 11, 2022 @ 18:25
    Chris Norwood
    0

    Thank you Huw, that's very kind of you! :)

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Oct 12, 2022 @ 07:53
    Huw Reddick
    100

    Hi Chris,

    This is the code for my middleware, it isn't exactly what you want but should get you started. Basically I have a usergroup picker on medi files/folders to restrict access for certain media files. The code below checks this against the current user to see if they can access the file, if not it responds with a 401 status.

            public class ProtectedMediaHandlerMiddleware
        {
            private readonly RequestDelegate _next;
    
            public ProtectedMediaHandlerMiddleware(RequestDelegate next)
            {
                _next = next;
            }
            public async Task Invoke(HttpContext context,IMediaService mediaService,IMemberManager memberManager, IOptionsSnapshot<CookieAuthenticationOptions> cookieOptionsSnapshot, IMemberGroupService memberGroupService)
            {
                if (!context.Request.Path.StartsWithSegments("/media"))
                {
                    await _next(context);
                    return;
                }
    
                if (IsBackofficeUser(context, cookieOptionsSnapshot))
                {
                    await _next(context);
                    return;
                }
                var protectedFolder = false;
                IMedia? parent = null;
                IMedia? mediaItem = mediaService.GetMediaByPath(context.Request.Path);
                if (mediaItem == null)
                {
                    await _next(context);
                    return;
                }
    
                parent = mediaService.GetParent(mediaItem.Id);
    
                var isAllowed = false;
    
                while (parent != null)
                {
                    if (parent.Properties.TryGetValue("authorisedRoles", out IProperty prop))
                    {
                        var res = prop.Values.Where(p => p.EditedValue != null).Select(p => p.EditedValue.ToString()).FirstOrDefault();
                        if (!string.IsNullOrEmpty(res))
                        {
                            var allowedFolderGroupStrs = res.Split(',',StringSplitOptions.RemoveEmptyEntries);
                            protectedFolder = allowedFolderGroupStrs.Any();
                            var member = memberManager.GetCurrentMemberAsync().Result;
                            if(member != null)
                            {
                                var roles =  memberManager.GetRolesAsync(member).Result;
                                foreach (var memberGroupStr in allowedFolderGroupStrs)
                                {
                                    var memberGroup = memberGroupService.GetById(Convert.ToInt32(memberGroupStr));
                                    if (memberGroup != null)
                                    {
                                        isAllowed = roles.Contains(memberGroup.Name);
                                    }
                                    if (isAllowed)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    parent = mediaService.GetParent(parent.Id);
                }
                if (!protectedFolder)
                {
                    await _next(context);
                    return;
                }
                if (memberManager.IsLoggedIn() && isAllowed)
                {
                    await _next(context);
                    return;
                }
                // Stop processing the request and return a 401 Unauthorized response.
                context.Response.Clear();
                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
    
    
            }
            private bool IsBackofficeUser(HttpContext context, IOptionsSnapshot<CookieAuthenticationOptions> cookieOptionsSnapshot)
            {
                CookieAuthenticationOptions cookieOptions = cookieOptionsSnapshot.Get(Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
                string backOfficeCookie = context.Request.Cookies[cookieOptions.Cookie.Name!];
                AuthenticationTicket unprotected = cookieOptions.TicketDataFormat.Unprotect(backOfficeCookie!);
                ClaimsIdentity backOfficeIdentity = new ClaimsIdentity();
                if (unprotected != null)
                {
                    backOfficeIdentity = unprotected!.Principal.GetUmbracoIdentity();
                }
                return backOfficeIdentity.IsAuthenticated;
            }
        }
    
    public static class MiddleWareExtensions
    {
        public static IApplicationBuilder ProtectedMediaHandler(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<ProtectedMediaHandlerMiddleware>();
        }
    
    }
    

    Then in your startup.cs you can add app.ProtectedMediaHandler(); to register it.

  • Chris Norwood 131 posts 642 karma points
    Oct 12, 2022 @ 08:00
    Chris Norwood
    0

    Thanks Huw,

    I'd managed to get about as far as that (with the realisation I'd have to use the IMediaService rather than a cache) - my concern is with the IMediaService - I think the default implementation goes directly to the database (this is a very large site with ~100k media items and it has a public site too, which can't slow down while we do this check - the "old" way of getting media via a controller let me use the cache, which was much quicker).

    That said we're only interested in doing this for non-image files, so hopefully I can filter those requests out via the path and just let the normal MiddleWare handle it :)

    H5YR!

    Cheers,

    Chris.

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Oct 12, 2022 @ 08:19
    Huw Reddick
    0

    if you can do it based on type/path then you won't need to get the media itself so would be much quicker. If you do need the Umbraco object for the file, you should use umbracocontext, but not sure how you would do that based only on a path (you can with the media service)

  • Chris Norwood 131 posts 642 karma points
    Oct 12, 2022 @ 08:23
    Chris Norwood
    0

    Yes, I think that's my problem - I need the object to check the properties of the MediaType so I can check a) if it's the right sort of media and b) if it's set to Internal.

    I can't get that without going to the MediaService at this point in the pipeline (but I can filter out images via the path and all of these documents should be in one of a couple of folders so I could possibly check for that too, so that might be the most appropriate solution - we only want to go to the media service if we know we need to, if that makes sense.).

    I've asked Umbraco Support (we're a Gold Partner) if there's some way of overriding the route as there was in Umbraco 8 - I'll post here if they come back to me, but in the meantime thanks for confirming my suspicions about how I'd have to do it :)

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Oct 12, 2022 @ 08:32
    Huw Reddick
    0

    You may be able to check the contenttype in the request header perhaps

  • Chris Norwood 131 posts 642 karma points
    Oct 13, 2022 @ 13:00
    Chris Norwood
    1

    So I got a response from Umbraco Support and after a bit of effort I've got it mostly working (it doesn't seem to hit the code on every request at the moment), but it's...quite involved...

    (We have a very simple use case for this, fortunately - there's only one member group and users can only log on if they're in that group, so if they're logged on at all they're OK to access an internal document)

        public class ProtectedMediaMiddleware: IMiddleware
    {
            private readonly IPublishedSnapshotService _snapshotService;
            private readonly IMemberManager _memberManager;
            private readonly IUmbracoContextFactory _umbracoContextFactory;
    
        public ProtectedMediaMiddleware(IPublishedSnapshotService snapshotService, IMemberManager memberManager, IUmbracoContextFactory umbracoContextFactory)
        {
            _snapshotService = snapshotService;
            _memberManager = memberManager;
            _umbracoContextFactory = umbracoContextFactory;
        }
    
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            //If its not media, or it is media but it's an image, or it's a back office request, move on:
            if (!context.Request.Path.StartsWithSegments("/media") || context.Request.Headers.ContentType.Contains("image") || context.Request.IsBackOfficeRequest())
            {
                await next(context);
                return;
            }
    
            try
            {
                using var _ = _umbracoContextFactory.EnsureUmbracoContext();
                var snapshot = _snapshotService.CreatePublishedSnapshot(null);
                var mediaCache = snapshot.Media;
                var mediaXpath = "//internalDocument[@isDoc]";
                var allMedia = snapshot.Media.GetByXPath(mediaXpath).Cast<InternalDocument>().ToList();
                //var media = snapshot.Media.GetSingleByXPath(mediaXpath);
                if (allMedia.Any(x => x.Url().Contains(context.Request.Path)))
                {
                    var media = allMedia.FirstOrDefault(x => x.Url() == context.Request.Path);
                    if (media != null)
                    {
                        if (media.IsDocumentType("internalDocument"))
                        {
                            if (media.Value<string>("Access").Contains("Internal") && !_memberManager.IsLoggedIn())
                            {
                                // Stop processing the request and return a 401 response.
                                context.Response.StatusCode = 401;
                                await Task.FromResult(0);
                                return;
                            }
                        }
                    }
                }
            }
            catch(Exception ex)
            { 
                //swallow the exception - we want to continue, not crash.
            }
    
            await next(context);
            return;
        }
    

    In Startup:

    app.UseUmbraco()
                .WithCustomMiddleware(u =>
                {
                    // You control the pre pipeline calls, if you do not include this
                    // then packages IUmbracoPipelineFilter will not execute
                    u.RunPrePipeline();
    
                    // You can call the method to register the default middleware
                    // that Umbraco installs. You could then register custom
                    // middleware before or after this call.
                    u.UseProtectedMediaMiddleware();
                    u.RegisterDefaultRequiredMiddleware();
    
    
                    // ELSE, you control the entire pipeline, i.e.
                    //u.UseUmbracoCoreMiddleware();
                    // TODO: Add the rest ...
                    //u.AppBuilder.UseStatusCodePages();
                    //u.AppBuilder.UseImageSharp();
                    //u.AppBuilder.UseStaticFiles();
                    //u.AppBuilder.UseUmbracoPlugins();
                    // etc ...
    
                    // You control the post pipeline calls, if you do not include this
                    // then packages IUmbracoPipelineFilter will not execute
                    u.RunPostPipeline();
    
                    // Now include the Umbraco request middleware
                    u.UseBackOffice();
                    u.UseWebsite();
    
                })
                //.WithMiddleware(u =>
                //{
                //    u.UseProtectedMediaMiddleware();
                //    u.UseBackOffice();
                //    u.UseWebsite();
                //})
                .WithEndpoints(u =>
                {
                    u.UseInstallerEndpoints();
                    u.UseBackOfficeEndpoints();
                    u.UseWebsiteEndpoints();
                });
    

    (See https://github.com/umbraco/Umbraco-CMS/pull/10702 for details)

    I'm going to see if I can tidy the XPATH up a bit (it's the only way I could think of to easily get the data without going to the database - even getting all 1000 or so documents it's orders of magnitude faster than IMediaService).

    Thanks for your help Huw - I hope the above is useful :)

  • Nick 42 posts 155 karma points
    Feb 29, 2024 @ 08:26
    Nick
    0

    Hi Chris,

    thanks for the example code, can you advise what else needs registering in the startup.cs class / what extension method needs creating as the

    u.UseProtectedMediaMiddleware();

    give a build error of:

    Error CS1061 'IUmbracoApplicationBuilderContext' does not contain a definition for 'UseProtectedMediaMiddleware' and no accessible extension method 'UseProtectedMediaMiddleware' accepting a first argument of type 'IUmbracoApplicationBuilderContext' could be found (are you missing a using directive or an assembly reference?)

    thanks,

    Nick

  • Chris Norwood 131 posts 642 karma points
    Feb 29, 2024 @ 08:42
    Chris Norwood
    0

    Hi Nick,

    Ah, that's just an extension method on the builder context, used to register the required middleware, which looks like this:

        public static class ProtectedMediaMiddlewareExtension
    {
        public static IUmbracoApplicationBuilderContext UseProtectedMediaMiddleware(this IUmbracoApplicationBuilderContext builder)
        {
            builder.AppBuilder.UseMiddleware<ProtectedMediaMiddleware>();
            return builder;
        }
    }
    

    Hope that helps! :)

    Chris.

  • Nick 42 posts 155 karma points
    Feb 29, 2024 @ 13:49
    Nick
    0

    That helps and has fixed that issue, thanks.

    I am now getting the following:

    InvalidOperationException: No service for type 'ProjectName.ProtectedMediaMiddleware' has been registered.

    How should the class be added to the services collection?

    Sorry for the further question but can't see any documentation at https://docs.umbraco.com/umbraco-cms/ relating to adding middleware.

  • Chris Norwood 131 posts 642 karma points
    Feb 29, 2024 @ 14:10
    Chris Norwood
    0

    Hi Nick,

    For a middleware class (at least in .Net 6, which is where this was built) there should be no need to add the service to the services collection - just registering it with the middleware pipeline should be enough (it just needs the correct methods etc). - it's possible this has changed in Umbraco 13/.NET 8

    In terms of our setup (this is no longer our project) we ended up with just this code in the Startup.cs Configure method:

                app.UseUmbraco()
                .WithMiddleware(u =>
                {
                    u.UseProtectedMediaMiddleware();
                    u.UseBackOffice();
                    u.UseWebsite();
                })
                .WithEndpoints(u =>
                {
                    u.UseInstallerEndpoints();
                    u.UseBackOfficeEndpoints();
                    u.UseWebsiteEndpoints();
                });
    

    Sorry I can't be more help on that bit - the class doesn't implement an interface and isn't registered for DI.

    If it's definitely a requirement to register it, then probably the following would do it:

    services.AddScoped<ProtectedMediaMiddleware, ProtectedMediaMiddleware>();

  • Nick 42 posts 155 karma points
    Feb 29, 2024 @ 15:42
    Nick
    0

    Thanks Chris, much appreciated.

    That has got it running but the Middleware is not being called when requesting a URL under the media folder. I have cleared the browser cache from dev tools.

    I have set breakpoints to check and the else isn't being hit when media items requested so some other middleware must be handling it before the ProtectMediaMiddleware

    if (!context.Request.Path.StartsWithSegments("/media") || context.Request.IsBackOfficeRequest())
    {
        await next(context); // breakpoint
        return;
    }
    else
    {
        int x = 1; // breakpoint
    }
    

    My startup Configure method contains:

    app.UseUmbraco()
        .WithMiddleware(u =>
        {
            u.UseProtectedMediaMiddleware();
            u.UseBackOffice();
            u.UseWebsite();
        })
        .WithEndpoints(u =>
        {
            u.UseInstallerEndpoints();
            u.UseBackOfficeEndpoints();
            u.UseWebsiteEndpoints();
        });
    
  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Feb 29, 2024 @ 15:57
    Huw Reddick
    0

    What is context.Request.Path when it hits the if?

  • Nick 42 posts 155 karma points
    Feb 29, 2024 @ 16:00
    Nick
    0

    Hi Huw,

    when loading a page such as 'About Us' at the root of the site the context.Request.Path = "/about-us/"

    However when I click on the one of the links on that page that points to a .pdf document in the media tree, neither breakpoint in the if or the else are hit.

  • Chris Norwood 131 posts 642 karma points
    Feb 29, 2024 @ 16:05
    Chris Norwood
    0

    Apologies, the version of the code we ended up using actually looked like this (this thread was a while ago!):

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Threading.Tasks;
    using Umbraco.Cms.Core.IO;
    using Umbraco.Cms.Core.PublishedCache;
    using Umbraco.Cms.Core.Security;
    using Umbraco.Cms.Core.Web;
    using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
    
    namespace Web.UI.Middleware
    {
        public class ProtectedMediaMiddleware
        {
    
            private readonly RequestDelegate _next;
    
            public ProtectedMediaMiddleware(RequestDelegate next)
            {
                _next = next;
            }
    
            public async Task InvokeAsync(HttpContext context, IPublishedSnapshotService snapshotService, IUmbracoContextFactory umbracoContextFactory,
                ILogger<ProtectedMediaMiddleware> logger, MediaFileManager mediaFileManager, IMemberManager memberService)
            {
                //If its not media, or it is media but it's an image, or it's a back office request, move on:
                if (!context.Request.Path.StartsWithSegments("/media") || context.Request.Headers.ContentType.Contains("image") || context.Request.IsBackOfficeRequest() || IsImage(context.Request.Path, mediaFileManager))
                {
                    await _next(context);
                    return;
                }
    
                logger.LogDebug("A file was requested from: {path}", context.Request.Path);
                try
                {
                    using var _ = umbracoContextFactory.EnsureUmbracoContext();
                    var snapshot = snapshotService.CreatePublishedSnapshot(null);
                    var mediaCache = snapshot.Media;
                    var mediaXpath = "//internalDocument[@isDoc]";
                    var allMedia = snapshot.Media.GetByXPath(mediaXpath).Cast<InternalDocument>().ToList();
                    //var media = snapshot.Media.GetSingleByXPath(mediaXpath);
                    if (allMedia.Any(x => x.Url().Contains(context.Request.Path)))
                    {
                        var media = allMedia.FirstOrDefault(x => x.Url() == context.Request.Path);
                        if (media != null)
                        {
                            if (media.IsDocumentType("internalDocument"))
                            {
                                //apparently the user is never actually logged in this way...
                                //var userName = context.User.Identity.GetUserName();
                                //var idToken = await context.GetTokenAsync("UmbracoMembers.OpenIdConnect", "id_token");
                                //var accessToken = await context.GetTokenAsync("UmbracoMembers.OpenIdConnect", "access_token");
                                //var userId = new Guid(userGuid);
                                //var currentUser = await memberService.GetCurrentMemberAsync();
                                var authResult = await context.AuthenticateAsync();
    
                                //memberService.find
                                //var isLoggedIn = await memberService.;
                                //memberManager.IsLoggedIn();
                                //context.User?.Identity?.IsAuthenticated ?? false;
                                if (media.Value<string>("Access").Contains("Internal") && !authResult.Succeeded)
                                {
                                    //Stop processing the request and return a 401 response; user is not authorised
                                    context.Response.StatusCode = 401;
                                    await Task.FromResult(0);
                                    return;
                                }
                            }
                        }
                    }
                }
                catch(Exception ex)
                {
    
                    try
                    {
                        logger.LogError(ex, "Failed to get file {file}", context?.Request?.Path);
                    }
                    catch
                    {
                        //swallow the second exception - we want to continue, not crash.
                    }
    
                    await Task.FromResult(0);
                    return;
                }
    
                await _next(context);
                return;
            }
    
            private bool IsImage(string path, MediaFileManager mediaFileManager)
            {
                //this should probably be our list of allowed media extensions from startup?
                var imageExtensions = new List<string>() { "jpg", "jpeg", "png", "mp4", "svg", "bmp", "webp", "gif" };
    
                //var fileStream = new FileStream(Server.MapPath(mediaPath), FileMode.Open);
                var file = mediaFileManager.FileSystem.GetFullPath(path); //_mediaFileSystem.GetFullPath(mediaPath);
                var extension = Path.GetExtension(file).Replace(".", String.Empty);
                //var fileInfo = new FileInfo(file);
                if (imageExtensions.Contains(extension))
                {
                    return true;
                }
    
                return false;
            }
        }
    }
    

    So we were looking for an explicit type of media (InternalDocument) to see whether we should do the check at all, but the above approach should still work

  • Nick 42 posts 155 karma points
    Feb 29, 2024 @ 16:29
    Nick
    0

    Thanks Chris, I have amended the class so it no longer inherits from IMiddleware but get the following:

    Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: ProjectName.ProtectedMediaMiddleware Lifetime: Scoped ImplementationType: ProjectName.ProtectedMediaMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'ProjectName.ProtectedMediaMiddleware'.)

    EDIT : This was fixed by removing the service.AddScoped line from startup. The project now builds ok.

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Feb 29, 2024 @ 17:56
    Huw Reddick
    0

    Hi Nick,

    is your middleware now working?

  • Nick 42 posts 155 karma points
    Mar 01, 2024 @ 08:15
    Nick
    0

    Hi Huw,

    no, it is not being triggered when requesting items under the media folder.

    It is being triggered for umbraco pages.

    thanks,

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Mar 01, 2024 @ 08:39
    Huw Reddick
    0

    Sounds like a routing issue, could you post the contents of your startup.cs file

  • Nick 42 posts 155 karma points
    Mar 01, 2024 @ 08:42
    Nick
    0
    namespace MyProjectName
    {
        public class Startup
        {
            private readonly IWebHostEnvironment _env;
            private readonly IConfiguration _config;
    
            /// <summary>
            /// Initializes a new instance of the <see cref="Startup" /> class.
            /// </summary>
            /// <param name="webHostEnvironment">The web hosting environment.</param>
            /// <param name="config">The configuration.</param>
            /// <remarks>
            /// Only a few services are possible to be injected here https://github.com/dotnet/aspnetcore/issues/9337.
            /// </remarks>
            public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration config)
            {
                _env = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment));
                _config = config ?? throw new ArgumentNullException(nameof(config));
            }
    
            /// <summary>
            /// Configures the services.
            /// </summary>
            /// <param name="services">The services.</param>
            /// <remarks>
            /// This method gets called by the runtime. Use this method to add services to the container.
            /// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940.
            /// </remarks>
            public void ConfigureServices(IServiceCollection services)
            {
    
                services.AddUmbraco(_env, _config)
                    .AddBackOffice()
                    .AddWebsite()
                    .AddComposers()
                    .Build();
            }
    
            /// <summary>
            /// Configures the application.
            /// </summary>
            /// <param name="app">The application builder.</param>
            /// <param name="env">The web hosting environment.</param>
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
    
                // custom middleware to add security related Response Headers
                app.Use(async (context, next) =>
                {
                    // https://docs.umbraco.com/umbraco-cms/extending/health-check/guides/clickjackingprotection
                    context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
                    // https://docs.umbraco.com/umbraco-cms/extending/health-check/guides/contentsniffingprotection
                    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
                    // https://docs.umbraco.com/umbraco-cms/extending/health-check/guides/crosssitescriptingprotection
                    context.Response.Headers.Add("X-Xss-Protection", "1; mode=block");
                    await next();
                });
    
    
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
    
                app.UseHttpsRedirection();
    
                app.UseUmbraco()
                    .WithMiddleware(u =>
                    {
                        u.UseProtectedMediaMiddleware();
                        u.UseBackOffice();
                        u.UseWebsite();
                    })
                    .WithEndpoints(u =>
                    {
                        u.UseInstallerEndpoints();
                        u.UseBackOfficeEndpoints();
                        u.UseWebsiteEndpoints();
                    });
            }
        }
    }
    
  • Nick 42 posts 155 karma points
    Mar 01, 2024 @ 08:51
    Nick
    0

    My program.cs is as follows. Could webBuilder.UseStaticWebAssets() have an impact as it is called before UseStartup?

    public class Program
    {
        public static void Main(string[] args)
            => CreateHostBuilder(args)
                .Build()
                .Run();
    
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureUmbracoDefaults()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStaticWebAssets();
                    webBuilder.UseStartup<Startup>();
                });
    }
    
  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Mar 01, 2024 @ 09:37
    Huw Reddick
    100

    Mine are a little different to yours. I don't have a UseStaticWebAssets in my program.cs and my middleware class is declared differently in startup.cs

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            app.Use(async (context, next) =>
            {
                context.Response.Headers.Add("X-Xss-Protection", "1; mode=block");
                context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000");
                context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
                context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
                await next();
    
            });
    
            app.UseAuthentication();
            // Create branch to the ProtectedMediaHandler. 
            // All requests ending containing media will follow this branch.
            app.ProtectedMediaHandler();
    
            app.UseRouting();
    
            app.UseUmbraco()
                .WithMiddleware(u =>
                {
                    u.UseBackOffice();
                    u.UseWebsite();
                })
                .WithEndpoints(u =>
                {
                    u.UseInstallerEndpoints();
                    u.UseBackOfficeEndpoints();
                    u.UseWebsiteEndpoints();
                });
    
        }
    
  • Nick 42 posts 155 karma points
    Mar 01, 2024 @ 10:13
    Nick
    0

    Thanks Huw, I didn't need to amend my program.cs but changed how the middleware class is included in startup to match yours and this is now successfully firing the middleware.

    Thanks for your assistance :0).

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Mar 01, 2024 @ 10:16
    Huw Reddick
    0

    your'e welcome

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Oct 13, 2022 @ 17:10
    Huw Reddick
    0

    Thanks for the update.

  • Nick 42 posts 155 karma points
    Feb 29, 2024 @ 16:47
    Nick
    0

    Still not hitting the middleware class for items in the media folder with Chris's updated code.

    Breakpoint in the middleware is hit if URL is a content page.

  • Chris Norwood 131 posts 642 karma points
    Feb 29, 2024 @ 16:48
    Chris Norwood
    0

    Is this an Umbraco 13 site Nick? It's possible that things have changed significantly if so - this was implemented in Umbraco 10, which was .NET 6; it may be that there's a separate media pipeline or something like that now?

  • Nick 42 posts 155 karma points
    Feb 29, 2024 @ 16:57
    Nick
    0

    It is an Umbraco 10 site.

    The only other modifications to startup.cs I have made are at the start of the Configure method but can't see they should change the pipeline of the requests for the media.

    // custom middleware to add security related Response Headers
    app.Use(async (context, next) =>
    {
        // https://docs.umbraco.com/umbraco-cms/extending/health-check/guides/clickjackingprotection
        context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
        // https://docs.umbraco.com/umbraco-cms/extending/health-check/guides/contentsniffingprotection
        context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
        // https://docs.umbraco.com/umbraco-cms/extending/health-check/guides/crosssitescriptingprotection
        context.Response.Headers.Add("X-Xss-Protection", "1; mode=block");
        await next();
    });
    
Please Sign in or register to post replies

Write your reply to:

Draft