Skip to content

Instantly share code, notes, and snippets.

@alirobe
Last active November 23, 2023 02:36
Show Gist options
  • Select an option

  • Save alirobe/2595fbe2d5d5fc1ef2f181d2ca18ec1f to your computer and use it in GitHub Desktop.

Select an option

Save alirobe/2595fbe2d5d5fc1ef2f181d2ca18ec1f to your computer and use it in GitHub Desktop.

Revisions

  1. alirobe revised this gist Nov 23, 2023. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion credit.txt
    Original file line number Diff line number Diff line change
    @@ -2,4 +2,6 @@ Original code here: https://giacomodeliberali.com/blog/umbraco-protect-media-ite
    This is the result of me modifying things and trying to get it working in Umbraco 9 cloud.

    One major difference: I decided to redirect all ~/media folder requests to /secure, and handle it there.
    This is because I had trouble hijacking /media in umbraco cloud, probably due to my own ignorance.
    This is because I had trouble hijacking /media in umbraco cloud, probably due to my own ignorance.

    You'll need to disable CDN cache entirely in Umbraco Cloud for this to work, otherwise I think media requests are handled by Cloudflare.
  2. alirobe revised this gist Mar 7, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions SecureMediaController.cs
    Original file line number Diff line number Diff line change
    @@ -18,13 +18,13 @@ public SecureMediaController(IMediaService mediaService, IMemberService memberSe
    _memberService = memberService;
    _mediaFileManager = mediaFileManager;
    }

    public ActionResult Index(string id, string file)
    {
    // reconstruct the media path and retrieve it
    string mediaPath = $"/media/{id}/{file}";
    var media = _mediaService.GetMediaByPath(mediaPath);

    if (media == null)
    return NotFound();

  3. alirobe revised this gist Mar 7, 2022. No changes.
  4. alirobe revised this gist Mar 5, 2022. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions SecureMediaController.cs
    Original file line number Diff line number Diff line change
    @@ -46,6 +46,8 @@ public ActionResult Index(string id, string file)
    var mimeType = MimeTypes.GetMimeType(file);
    return new FileStreamResult(fileStreamReader, mimeType);
    }

    return NotFound();
    }
    }
    }
  5. alirobe revised this gist Mar 5, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions SecureMediaController.cs
    Original file line number Diff line number Diff line change
    @@ -12,11 +12,11 @@ public class SecureMediaController : Controller
    private readonly IMemberService _memberService;
    private readonly MediaFileManager _mediaFileManager;

    public SecureMediaController(IMediaService mediaService, IMemberService memberService, MediaFileManager _mediaFileManager)
    public SecureMediaController(IMediaService mediaService, IMemberService memberService, MediaFileManager mediaFileManager)
    {
    _mediaService = mediaService;
    _memberService = memberService;
    _mediaFileManager = _mediaFileManager;
    _mediaFileManager = mediaFileManager;
    }

    public ActionResult Index(string id, string file)
  6. alirobe revised this gist Mar 3, 2022. 2 changed files with 13 additions and 11 deletions.
    13 changes: 2 additions & 11 deletions SecureMediaController.cs
    Original file line number Diff line number Diff line change
    @@ -6,9 +6,6 @@

    namespace UmbracoProject.Controllers
    {
    /// <summary>
    /// Handles all request to media items in order to check is they need auth or not
    /// </summary>
    public class SecureMediaController : Controller
    {
    private readonly IMediaService _mediaService;
    @@ -22,13 +19,7 @@ public SecureMediaController(IMediaService mediaService, IMemberService memberSe
    _mediaFileManager = _mediaFileManager;
    }


    /// <summary>
    /// The action called when processing a media request
    /// </summary>
    /// <param name="id">The umbraco file id</param>
    /// <param name="file">The umbraco filename with extension</param>
    public async Task<ActionResult> Index(string id, string file)
    public ActionResult Index(string id, string file)
    {
    // reconstruct the media path and retrieve it
    string mediaPath = $"/media/{id}/{file}";
    @@ -38,7 +29,7 @@ public async Task<ActionResult> Index(string id, string file)
    return NotFound();


    // N.B. THE FOLLOWING BIT IS UNTESTED AS I AM DOING SOMETHING ELSE HERE
    // THE FOLLOWING BIT IS UNTESTED AS I AM DOING SOMETHING ELSE HERE
    if (media.IsProtectedMedia())
    {
    // check if the user has right roles
    11 changes: 11 additions & 0 deletions appsettings.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    for Umbraco Cloud, get these values from Umbraco Cloud Project Manager -> Settings -> Connection Details -> Blob Storage Connection Details

    "Umbraco": {
    "Storage": {
    "AzureBlob": {
    "Media": {
    "ConnectionString": "BlobEndpoint=https://ucmediastoragewelive.blob.core.windows.net/;SharedAccessSignature=[4. Shared Access Signature, without the first question mark]",
    "ContainerName": "[3. Container Name GUID only, no curly braces]"
    }
    }
    },
  7. alirobe revised this gist Mar 3, 2022. 1 changed file with 19 additions and 17 deletions.
    36 changes: 19 additions & 17 deletions Startup.cs
    Original file line number Diff line number Diff line change
    @@ -1,22 +1,23 @@
    public void ConfigureServices(IServiceCollection services)
    {
    public void ConfigureServices(IServiceCollection services)
    {
    #pragma warning disable IDE0022 // Use expression body for methods
    services.AddUmbraco(_env, _config)
    .AddBackOffice()
    .AddWebsite()
    .AddComposers()
    // For Umbraco Cloud:
    .AddAzureBlobMediaFileSystem(options =>
    {
    options.ConnectionString = _config.GetValue("Umbraco:Storage:AzureBlob:Media:ConnectionString", "");
    options.ContainerName = _config.GetValue("Umbraco:Storage:AzureBlob:Media:ContainerName", "");
    })
    .Build();
    services.AddUmbraco(_env, _config)
    .AddBackOffice()
    .AddWebsite()
    .AddComposers()
    // For Umbraco Cloud:
    .AddAzureBlobMediaFileSystem(options =>
    {
    options.ConnectionString = _config.GetValue("Umbraco:Storage:AzureBlob:Media:ConnectionString", "");
    options.ContainerName = _config.GetValue("Umbraco:Storage:AzureBlob:Media:ContainerName", "");
    })
    .Build();
    #pragma warning restore IDE0022 // Use expression body for methods
    }
    // add the u.EndpointRouteBuilder.MapControllerRoute bit
    }

    app.UseUmbraco()
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    app.UseUmbraco()
    .WithMiddleware(u =>
    {
    u.UseBackOffice();
    @@ -38,4 +39,5 @@ public void ConfigureServices(IServiceCollection services)
    action = "Index"
    }
    );
    });
    });
    }
  8. alirobe revised this gist Mar 3, 2022. 2 changed files with 28 additions and 16 deletions.
    25 changes: 9 additions & 16 deletions SecureMediaController.cs
    Original file line number Diff line number Diff line change
    @@ -1,16 +1,8 @@
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.ViewEngines;
    using Microsoft.Extensions.Logging;
    using Umbraco.Cms.Core.Services;
    using Umbraco.Cms.Core.Web;
    using Umbraco.Cms.Web.Common.Controllers;
    using MimeKit;
    using Microsoft.AspNetCore.Hosting;
    using System.IO;
    using Umbraco.Cms.Core.Models;
    using Umbraco.Cms.Core.Security;
    using Umbraco.Cms.Web.Common.Security;
    using System.Threading.Tasks;
    using Umbraco.Cms.Core.IO;

    namespace UmbracoProject.Controllers
    {
    @@ -21,13 +13,13 @@ public class SecureMediaController : Controller
    {
    private readonly IMediaService _mediaService;
    private readonly IMemberService _memberService;
    private readonly IHostingEnvironment _hostingEnvironment;
    private readonly MediaFileManager _mediaFileManager;

    public SecureMediaController(IMediaService mediaService, IMemberService memberService, IHostingEnvironment hostingEnvironment)
    public SecureMediaController(IMediaService mediaService, IMemberService memberService, MediaFileManager _mediaFileManager)
    {
    _mediaService = mediaService;
    _memberService = memberService;
    _hostingEnvironment = hostingEnvironment;
    _mediaFileManager = _mediaFileManager;
    }


    @@ -58,10 +50,11 @@ public async Task<ActionResult> Index(string id, string file)
    }
    // UNTESTED BIT ENDS

    var mediaLocation = Path.Combine(_hostingEnvironment.WebRootPath, mediaPath);
    var fileStreamReader = new StreamReader(mediaLocation);
    return new FileStreamResult(fileStreamReader.BaseStream, MimeKit.MimeTypes.GetMimeType(file));

    if (_mediaFileManager.FileSystem.FileExists(mediaPath)) {
    var fileStreamReader = _mediaFileManager.FileSystem.OpenFile(mediaPath);
    var mimeType = MimeTypes.GetMimeType(file);
    return new FileStreamResult(fileStreamReader, mimeType);
    }
    }
    }
    }
    19 changes: 19 additions & 0 deletions Startup.cs
    Original file line number Diff line number Diff line change
    @@ -1,16 +1,35 @@
    public void ConfigureServices(IServiceCollection services)
    {
    #pragma warning disable IDE0022 // Use expression body for methods
    services.AddUmbraco(_env, _config)
    .AddBackOffice()
    .AddWebsite()
    .AddComposers()
    // For Umbraco Cloud:
    .AddAzureBlobMediaFileSystem(options =>
    {
    options.ConnectionString = _config.GetValue("Umbraco:Storage:AzureBlob:Media:ConnectionString", "");
    options.ContainerName = _config.GetValue("Umbraco:Storage:AzureBlob:Media:ContainerName", "");
    })
    .Build();
    #pragma warning restore IDE0022 // Use expression body for methods
    }
    // add the u.EndpointRouteBuilder.MapControllerRoute bit

    app.UseUmbraco()
    .WithMiddleware(u =>
    {
    u.UseBackOffice();
    u.UseWebsite();
    // For Umbraco Cloud:
    u.UseAzureBlobMediaFileSystem();
    })
    .WithEndpoints(u =>
    {
    u.UseInstallerEndpoints();
    u.UseBackOfficeEndpoints();
    u.UseWebsiteEndpoints();
    // For all sites:
    u.EndpointRouteBuilder.MapControllerRoute(
    "SecureMediaRoute",
    "secure/{id?}/{file?}",
  9. alirobe revised this gist Mar 3, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion SecureMediaController.cs
    Original file line number Diff line number Diff line change
    @@ -58,7 +58,7 @@ public async Task<ActionResult> Index(string id, string file)
    }
    // UNTESTED BIT ENDS

    var mediaLocation = Path.Combine(_hostingEnvironment.WebRootPath + mediaPath);
    var mediaLocation = Path.Combine(_hostingEnvironment.WebRootPath, mediaPath);
    var fileStreamReader = new StreamReader(mediaLocation);
    return new FileStreamResult(fileStreamReader.BaseStream, MimeKit.MimeTypes.GetMimeType(file));

  10. alirobe revised this gist Mar 3, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion web.Production.config
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@
    <rewrite xdt:Transform="InsertIfMissing">
    <rules>
    <rule xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" name="lock media folder" stopProcessing="true">
    <match url="~/media/(.*)" />
    <match url="^media/(.*)" />
    <action type="Redirect" url="/secure/{R:1}" appendQueryString="true" redirectType="Temporary" />
    </rule>
    </rules>
  11. alirobe revised this gist Mar 2, 2022. No changes.
  12. alirobe revised this gist Mar 2, 2022. 1 changed file with 0 additions and 3 deletions.
    3 changes: 0 additions & 3 deletions SecureMediaController.cs
    Original file line number Diff line number Diff line change
    @@ -30,9 +30,6 @@ public SecureMediaController(IMediaService mediaService, IMemberService memberSe
    _hostingEnvironment = hostingEnvironment;
    }

    /// <summary>
    /// The umbraco media service
    /// </summary>

    /// <summary>
    /// The action called when processing a media request
  13. alirobe created this gist Mar 2, 2022.
    70 changes: 70 additions & 0 deletions SecureMediaController.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,70 @@
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.ViewEngines;
    using Microsoft.Extensions.Logging;
    using Umbraco.Cms.Core.Services;
    using Umbraco.Cms.Core.Web;
    using Umbraco.Cms.Web.Common.Controllers;
    using MimeKit;
    using Microsoft.AspNetCore.Hosting;
    using System.IO;
    using Umbraco.Cms.Core.Models;
    using Umbraco.Cms.Core.Security;
    using Umbraco.Cms.Web.Common.Security;
    using System.Threading.Tasks;

    namespace UmbracoProject.Controllers
    {
    /// <summary>
    /// Handles all request to media items in order to check is they need auth or not
    /// </summary>
    public class SecureMediaController : Controller
    {
    private readonly IMediaService _mediaService;
    private readonly IMemberService _memberService;
    private readonly IHostingEnvironment _hostingEnvironment;

    public SecureMediaController(IMediaService mediaService, IMemberService memberService, IHostingEnvironment hostingEnvironment)
    {
    _mediaService = mediaService;
    _memberService = memberService;
    _hostingEnvironment = hostingEnvironment;
    }

    /// <summary>
    /// The umbraco media service
    /// </summary>

    /// <summary>
    /// The action called when processing a media request
    /// </summary>
    /// <param name="id">The umbraco file id</param>
    /// <param name="file">The umbraco filename with extension</param>
    public async Task<ActionResult> Index(string id, string file)
    {
    // reconstruct the media path and retrieve it
    string mediaPath = $"/media/{id}/{file}";
    var media = _mediaService.GetMediaByPath(mediaPath);

    if (media == null)
    return NotFound();


    // N.B. THE FOLLOWING BIT IS UNTESTED AS I AM DOING SOMETHING ELSE HERE
    if (media.IsProtectedMedia())
    {
    // check if the user has right roles
    if (!this.HttpContext.User.IsInRole("YourRole"))
    {
    // otherwise redirect
    return Redirect(Defaults.LoginRoute);
    }
    }
    // UNTESTED BIT ENDS

    var mediaLocation = Path.Combine(_hostingEnvironment.WebRootPath + mediaPath);
    var fileStreamReader = new StreamReader(mediaLocation);
    return new FileStreamResult(fileStreamReader.BaseStream, MimeKit.MimeTypes.GetMimeType(file));

    }
    }
    }
    22 changes: 22 additions & 0 deletions Startup.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    // add the u.EndpointRouteBuilder.MapControllerRoute bit

    app.UseUmbraco()
    .WithMiddleware(u =>
    {
    u.UseBackOffice();
    u.UseWebsite();
    })
    .WithEndpoints(u =>
    {
    u.UseInstallerEndpoints();
    u.UseBackOfficeEndpoints();
    u.UseWebsiteEndpoints();
    u.EndpointRouteBuilder.MapControllerRoute(
    "SecureMediaRoute",
    "secure/{id?}/{file?}",
    new {
    controller = "SecureMedia",
    action = "Index"
    }
    );
    });
    5 changes: 5 additions & 0 deletions credit.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    Original code here: https://giacomodeliberali.com/blog/umbraco-protect-media-items
    This is the result of me modifying things and trying to get it working in Umbraco 9 cloud.

    One major difference: I decided to redirect all ~/media folder requests to /secure, and handle it there.
    This is because I had trouble hijacking /media in umbraco cloud, probably due to my own ignorance.
    15 changes: 15 additions & 0 deletions web.Production.config
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    <?xml version="1.0" encoding="utf-8"?>
    <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <location>
    <system.webServer>
    <rewrite xdt:Transform="InsertIfMissing">
    <rules>
    <rule xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" name="lock media folder" stopProcessing="true">
    <match url="~/media/(.*)" />
    <action type="Redirect" url="/secure/{R:1}" appendQueryString="true" redirectType="Temporary" />
    </rule>
    </rules>
    </rewrite>
    </system.webServer>
    </location>
    </configuration>