How to make Sitecore media to be downloaded from authenticated users only?

Recently, I have had a requirement that besides the public assets, some Sitecore media items (e.g. pdf files, doc files, reports, etc.) must be protected and only allowed to access and download by authenticated users (using login feature) on the website. This requirement also required that there was a flexibility for CMS users when they work on the media upload.

Sitecore Template and Sitecore Security are powerful and I wanted to use them to accomplish this requirement. Now, let analyze to see the approach:

  1. Sitecore Template: we will create a custom media folder template inherited from “/sitecore/templates/System/Media/Media folder”.
  2. Sitecore Security: grant the permission to the custom media folder template above for “Anonymous” users so that if CMS users want any media items to be secured, they just upload them under this folder, no need to grant permission to individuals.
  3. To let end-users know which media items require login or not, we can leverage the property “ItemSecurity” from Sitecore.Data.Items to check if the current Context User can read or not. If can read, we return media URL for media item. If cannot, we return the login URL accordingly.

First of all, we will create custom media folder template:

 Now, we will grant the permission on this template for 2 Anonymous users. Select the “_Standard Values” and on the ribbon, navigate to Security tab -> Assign.

Next, we will create the secure folder and upload a testing PDF file as below:

Now, publish changes and try to access to this PDF item “Root_URL/-/media/files/secure/Test.pdf”. If everything works correctly, we will get the “access denied” message from Sitecore:

I will create a simple component to download PDF file. If the user is authenticated, render “Download” link. Otherwise, display “Please login to download”. The code-behind looks like this:

public class FileDownloadController : Controller
{
	private const string userServiceUserName = @"sitecore\InternalServiceUser";

	public FileDownloadController()
	{
	}

	/// <summary>
	/// The component for the file download.
	/// We should use Sitecore Account as Internal Service User to read the media item. From that, we can get the item security info
	/// </summary>
	/// <returns></returns>
	public ActionResult Document()
	{
		var dataSource = RenderingContext.Current.Rendering.DataSource;
		if (string.IsNullOrWhiteSpace(dataSource))
			return new EmptyResult();

		var userSvcAcc = Sitecore.Security.Accounts.User.Exists(userServiceUserName) ? Sitecore.Security.Accounts.User.FromName(userServiceUserName, false) : null;
		if (userSvcAcc == null)
			return new EmptyResult();

		Item dataSourceItem;
		using (new UserSwitcher(userSvcAcc))
		{
			dataSourceItem = Sitecore.Context.Database.GetItem(dataSource);
		}
		if (dataSourceItem.Security.CanRead(Sitecore.Context.User))
		{
			string url = Sitecore.StringUtil.EnsurePrefix('/', HashingUtils.ProtectAssetUrl(MediaManager.GetMediaUrl(dataSourceItem)));
			return View(new FileDownloadModel() { AccessDenied = false, Url = url });
		}
		return View(new FileDownloadModel() { AccessDenied = true, Url = "/login" });
	}
}

As we limited the access for the media items, we should create a Sitecore User account to get the media item info in the code so that we can check its Security. In this case, I created the user “sitecore\InternalServiceUser” with admin right.

dataSourceItem.Security.CanRead(Sitecore.Context.User): checks whether the current context user can read the media item or not.

The View will be:

@model WebApplication1.Models.FileDownloadModel

<a href="@Model.Url">@(Model.AccessDenied ? "Please login to download" : "Download")</a>

Deploy the changes and we can see the result. Clicking on “Please login to download” will bring user to login page

To test the login, I would need to create a testing “extranet” account. Also, create a Login component, add to login page like this.

  1. Username: testUser
  2. Password: test123@
public class AccountsController : Controller
{
	public AccountsController()
	{
	}

	/// <summary>
	/// The component for auto login
	/// We create with GET method for testing only
	/// We should display the login form and handle the submission from user
	/// </summary>
	/// <returns></returns>
	public ActionResult AutoLogin()
	{
		var success = Sitecore.Security.Authentication.AuthenticationManager.Login(@"extranet\testUser", "test123@");
		if (success)
			// After login successfully, redirect users to home page to download the document
			return Redirect("/");
		return new HttpUnauthorizedResult();
	}
}

Deploy the code and publish all changes, we will get the result as below:

Got issues?

Please leave a message or drop me an email at [email protected].

Happy Sitecore coding!

Related Articles:

Leave a Comment