I am trying to write my own media protection system for Umbraco, before any one says purchase one I am not going down that route so save your typing.
Is there any way to remove the route for Media/{id}/{file}
I have tried both with a surface controller and a IHttpHandler when I put a url in like:
https://localhost:44347/media/mjmdicul/1-copy.pdf It not hitting the handler or the controller it going striate to the PDF and I do not want that I want to intercept the URL for the Media folder and then decide if I want to allow download of the file or block it.
As I said I do not want to pay for a 3rd party tool, I want to be able to do this in code.
Here is the code for my Routing:
public class RegisterDependencies : IUserComposer
{
public void Compose(Composition composition)
{
public class ProtectedMediaHttpHandler : IHttpHandler
{
/// <summary>
/// Gets a value indicating whether another request can use the IHttpHandler instance.
/// </summary>
public bool IsReusable => false;
/// <summary>
/// Method to return the file
/// </summary>
/// <param name="context"></param>
/// <param name="strFile"></param>
/// <returns></returns>
HttpContext SendContentTypeAndFile(HttpContext context, string strFile)
{
context.Response.ContentType = GetContentType(strFile);
context.Response.TransmitFile(strFile);
context.Response.End();
return context;
}
/// <summary>
/// Processes the Http request
/// </summary>
/// <param name="context">HttpContext</param>
/// <remarks>
/// Returns the requested file or Http 403 if the file is protected and user not logged in
/// </remarks>
public void ProcessRequest(HttpContext context)
{
var user = new HttpContextWrapper(HttpContext.Current).User;
var ticket = new HttpContextWrapper(HttpContext.Current).GetUmbracoAuthTicket();
//Allow Bacoffice users if authenticated
bool isAllowed = ticket != null;
if (isAllowed) // we are allowed so return the file
{
string requestedFile = context.Server.MapPath(context.Request.FilePath);
SendContentTypeAndFile(context, requestedFile);
return;
}
List<KeyValuePair<string, object>> parameters = new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("username", user.Identity.Name),
new KeyValuePair<string, object>("mediaPath", context.Request.FilePath)
};
ApiHelper apiHelper = new ApiHelper();
var port = (context.Request.Url.Port != 443) ? ":" + context.Request.Url.Port : "";
string url = apiHelper.BuildApiUrl(
domainAddress: "https://" + context.Request.Url.Host + port,
apiLocation: "Umbraco/Api/",
controllerName: "ProtectedMediaApi",
methodName: "IsAllowed",
parameters: parameters);
if(!isAllowed)
{
try
{
isAllowed = apiHelper.GetResultFromApi<bool>(url);
}
catch (Exception e)
{
throw new Exception($"{e.Message} : {context.Request.FilePath}");
}
}
if (isAllowed)
{
string requestedFile = context.Server.MapPath(context.Request.FilePath);
SendContentTypeAndFile(context, requestedFile);
return;
}
else
{
context.Response.Status = "403 Forbidden";
context.Response.StatusCode = 403;
return;
}
}
/// <summary>
/// Method to return the files Content/MimeType
/// </summary>
/// <param name="filename"></param>
/// <returns>A string representing the mime type of the file</returns>
private string GetContentType(string filename)
{
FileInfo fileinfo = new FileInfo(filename);
if (fileinfo.Exists)
{
string res;
switch (fileinfo.Extension.Remove(0, 1).ToLower())
{
case "pdf":
{
res = "application/pdf";
break;
}
case "doc":
{
res = "application/msword";
break;
}
case "docx":
{
res = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
break;
}
case "xls":
{
res = "application/vnd.ms-excel";
break;
}
case "xlsx":
{
res = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
break;
}
case "afdesign":
{
res = "application/octet-stream";
break;
}
default:
res = MimeMapping.GetMimeMapping(filename);
break;
}
return res;
}
return null;
}
/// <summary>
/// Helper class to send Api calls
/// </summary>
private class ApiHelper
{
/// <summary>
/// Builds URL for calling an API method
/// </summary>
/// <param name="domainAddress">The domain of the API</param>
/// <param name="controllerName">The name of the controller</param>
/// <param name="methodName">The name of the action to call</param>
/// <param name="parameters">List of keyvalue parameters to send</param>
/// <param name="apiLocation">Application folder of API (if not at root)</param>
/// <returns>A string representing the API URL</returns>
public string BuildApiUrl(string domainAddress, string controllerName, string methodName, List<KeyValuePair<string, object>> parameters, string apiLocation)
{
StringBuilder url = new StringBuilder();
url.Append($"{domainAddress}/{apiLocation}{controllerName}/{methodName}");
if (parameters != null && parameters.Count > 0)
{
int parameterCount = parameters.Count;
for (int i = 0; i < parameterCount; i++)
{
url.Append(i == 0 ? "?" : "&");
url.Append($"{parameters[i].Key}={parameters[i].Value.ToString()}");
}
}
return url.ToString();
}
/// <summary>
/// Returns the response from an API call
/// </summary>
/// <typeparam name="T">Type of result to return</typeparam>
/// <param name="url">API url</param>
/// <returns>An object of the Type specified.</returns>
public T GetResultFromApi<T>(string url)
{
using (HttpClient httpClient = new HttpClient())
{
Task<string> response = httpClient.GetStringAsync(url);
var res = response.Result;
var test = Task.Factory.StartNew(() => JsonConvert.DeserializeObject<T>(res));
return test.Result;
}
}
}
}
It calls an API method which returns true/false depending on user access
Are you still registerring a custom route? this maybe what is causing your problem.
You should just require the IHttpHandler and the web.config change in the media folder, nothing else is required. If you have done something with the routing then it is probably bypassing the web.config and therfore the handler.
Could you post the code for your handler and your media web.config?
Ah, yes, I have had this issue after succesfully grabbing the pdf while logged in, it will then be in your browser cache so you need to force it to refetch :) otherwise it just grabs it from the cache, it will do this in any browser not just chrome
Overriding Media/{id}/{file}
Hi,
I am trying to write my own media protection system for Umbraco, before any one says purchase one I am not going down that route so save your typing.
Is there any way to remove the route for Media/{id}/{file}
I have tried both with a surface controller and a IHttpHandler when I put a url in like:
https://localhost:44347/media/mjmdicul/1-copy.pdf It not hitting the handler or the controller it going striate to the PDF and I do not want that I want to intercept the URL for the Media folder and then decide if I want to allow download of the file or block it.
As I said I do not want to pay for a 3rd party tool, I want to be able to do this in code.
Here is the code for my Routing:
public class RegisterDependencies : IUserComposer { public void Compose(Composition composition) {
public class RegisterCustomRouteComposer : ComponentComposer
Here what I have in my web config in the media folder for the IHTTPHandler
can't see what you have in your media web.config.
This is how I do it, I have an IHttphandler
It is referenced in the media folders web.config
Where MyApp is the namespace of your httphandler, there is no custome routing involved, just the change to the web.config in the media folder
This is the fullcode for my Httphandler
It calls an API method which returns true/false depending on user access
I have tried that using a IHTTPHandler, but if you read what I put I said once you put in a URL for a valid media file say
http://site/Media/1345/testme.pdf.
It is not hitting the IHTTPHandler even if the correct settings are in the web config in the media folder per the artical on code share
https://codeshare.co.uk/blog/how-to-protect-media-items-in-umbraco/
For some reason it not allowing the IHTTPHandler to fire if it finding the file first.
I tried it both with a HTTP Handler and a surface control in the latest version of V8 and it not working either way,
It is working fine for me, so not sure why it doesn't work for you
Are you still registerring a custom route? this maybe what is causing your problem.
You should just require the IHttpHandler and the web.config change in the media folder, nothing else is required. If you have done something with the routing then it is probably bypassing the web.config and therfore the handler.
Could you post the code for your handler and your media web.config?
I have it working with a IHTTPHandler.
Looks like some of it was down to caching in Chrome.
Ah, yes, I have had this issue after succesfully grabbing the pdf while logged in, it will then be in your browser cache so you need to force it to refetch :) otherwise it just grabs it from the cache, it will do this in any browser not just chrome
is working on a reply...