Hey Jason,
That would solve the selector problem, but it wouldn't really solve the suffix
problem though. But that one can be fixed in the same way although I would
expect the whitelisting to be a bit tougher in that one ;), seeing as suffix is
used as a dynamic variable most of the times. For my specific needs I did solve
it now by actually looking at the request and seeing if the modification date
equals the actual asset modification date. Thanks for all the help!
Resource resource = request.getResource();
if(resource instanceof NonExistingResource) {
LOG.info("Asset resource {} using the cdn selector does not exist",
resource.getPath());
response.sendError(404);
} else if(!"last-modified".equals(getSuffixSegment(request, 0))) {
LOG.info("Asset resource {} is not using the last modified suffix",
resource.getPath());
response.sendError(404);
} else if(resource.adaptTo(Asset.class) == null) {
LOG.info("Resource {} is not an asset", resource.getPath());
response.sendError(404);
} else
if(!String.valueOf(((Asset)resource.adaptTo(Asset.class)).getLastModified()).equals(getSuffixSegment(request,
1))) {
LOG.info("Asset resource {} does not have the same last modified date as
request: {}", resource.getPath(), getSuffixSegment(request, 1));
response.sendError(404);
} else {
RequestDispatcherOptions opts = new RequestDispatcherOptions();
opts.setReplaceSelectors("");
RequestDispatcher dispatcher = request.getRequestDispatcher(resource, opts);
if(dispatcher != null) {
dispatcher.forward(request, response);
} else {
LOG.error("Could not get request dispatcher for asset resource {}",
resource.getPath());
}
}
Greets
Roy
> On 1 Aug 2016, at 16:26, Jason Bailey <[email protected]> wrote:
>
> If we're talking dispatcher, then yes, this is the wrong list :) However if
> we focus on the general concern, which is that as a by-product of its design.
> There is an inherent exploit which can result in DOS attack when using a
> cache.
>
> One solution might be a generic selector filter, one that can accept a list
> of patterns to whitelist or blacklist.
>
> -----Original Message-----
> From: Roy Teeuwen [mailto:[email protected]]
> Sent: Saturday, July 30, 2016 6:58 AM
> To: [email protected]
> Subject: Re: Getting the actual resource from a request
>
> Hey Jason,
>
> Just come back to the dispatcher caching problem you saw. I think this is
> actually a global problem that happens anywhere, is there any rule in the
> dispatcher to be able stop this? Just to look at a fun real life example:
>
> http://wwwimages.adobe.com/content/dam/Adobe/images/shared/product_mnemonics/48x48/dreamweaver-no-shadow-48x48.png
>
> <http://wwwimages.adobe.com/content/dam/Adobe/images/shared/product_mnemonics/48x48/dreamweaver-no-shadow-48x48.png>
> http://wwwimages.adobe.com/content/dam/Adobe/images/shared/product_mnemonics/48x48/dreamweaver-no-shadow-48x48.png./test.jpg
>
> <http://wwwimages.adobe.com/content/dam/Adobe/images/shared/product_mnemonics/48x48/dreamweaver-no-shadow-48x48.png./test.jpg>
> http://wwwimages.adobe.com/content/dam/Adobe/images/shared/product_mnemonics/48x48/dreamweaver-no-shadow-48x48.png./test/another.jpg
>
> <http://wwwimages.adobe.com/content/dam/Adobe/images/shared/product_mnemonics/48x48/dreamweaver-no-shadow-48x48.png./test/another.jpg>
>
> It seems to make a new image in the dispatcher cache for every request. But I
> guess we would be on the wrong list to discuss something like this ;)
>
> Greets
> Roy
>
>
>> On 29 Jul 2016, at 15:04, Roy Teeuwen <[email protected]> wrote:
>>
>> Hey Jason,
>>
>> You are absolutely right, damn :)! It’s because I didn’t realise that the
>> .jpg didn’t count as extension. I’m going to keep it with a suffix though:
>> /content/dam/nice-image.jpg.cdn./mod-date/20160815/nice-image.jpg just so
>> that in the browser it still looks like the image is called nice-image.jpg
>> ;).
>>
>> And you are also right about the second part, sadly enough :P. So maybe it
>> would be safe to make the servlet after all, so that I can check the
>> mod-date suffix part and see if that asset actually exists with that last
>> modified date and only return it then, and else return a 404. This would
>> solve the caching possibility of infinite image issue!
>>
>> Thanks
>> Roy
>>
>>
>>> On 29 Jul 2016, at 14:31, Jason Bailey <[email protected]> wrote:
>>>
>>> Roy,
>>>
>>> I think you may slap your forehead on this one :) You had to
>>> implement a service because when you added the cdn between the image
>>> name and the jpeg. It broke the resource resolution. That's why you
>>> had problems with the request.getResource()
>>>
>>> Once you fixed it by moving the selector, the resource is identified. That
>>> means, it's found the image. You don't need to have a service. The Sling
>>> application will take your URL, identify the resource and return that
>>> resource.
>>>
>>> So if all you did was just append .%timestamp$. to the end of your
>>> assets it will do what you want it do
>>>
>>> content/dam/nice-image.jpg.20160815.
>>>
>>> This is a nice idea for cdn's but you need to be aware of a potential
>>> issue. I know, from a dispatcher point of view that the ability to add
>>> selectors to any resource and return the original resource is a potential
>>> attack vector. An example would be if that url you gave was behind a
>>> dispatcher that does caching. I could sit there and call that asset with a
>>> millisecond time stamp and each time I did it, it would be cached, and the
>>> cache would get larger. Until some point I have consumed all of your disk
>>> space.
>>>
>>> I'm assuming a similar type issue with cdn's
>>>
>>> -Jason
>>>
>>> -----Original Message-----
>>> From: Roy Teeuwen [mailto:[email protected]]
>>> Sent: Friday, July 29, 2016 8:14 AM
>>> To: [email protected]
>>> Subject: Re: Getting the actual resource from a request
>>>
>>> Hey all,
>>>
>>> Ok so I fixed it finally by doing the following:
>>>
>>> @SlingServlet(
>>> resourceTypes = "sling/servlet/default",
>>> selectors = "cdn"
>>> )
>>> @Service({Servlet.class})
>>> public class CDNAssetServlet extends SlingSafeMethodsServlet {
>>>
>>> private static final Logger LOG =
>>> LoggerFactory.getLogger(CDNAssetServlet.class);
>>>
>>> @Override
>>> protected void doGet(SlingHttpServletRequest request,
>>> SlingHttpServletResponse response) throws ServletException, IOException {
>>> Resource resource = request.getResource();
>>> if (resource instanceof NonExistingResource) {
>>> LOG.info("Asset resource {} using the cdn selector does not
>>> exist", resource.getPath());
>>> response.sendError(HttpServletResponse.SC_NOT_FOUND);
>>> } else {
>>> RequestDispatcherOptions opts = new RequestDispatcherOptions();
>>> opts.setReplaceSelectors("");
>>> RequestDispatcher dispatcher =
>>> request.getRequestDispatcher(resource, opts);
>>> if (dispatcher != null) {
>>> dispatcher.forward(request, response);
>>> } else {
>>> LOG.error("Could not get request dispatcher for asset resource
>>> {}", resource.getPath());
>>> }
>>> }
>>> }
>>> }
>>>
>>> And then using the url provided by Jason :) thanks!
>>> @Olivier
>>> Still don’t know what you meant by just serving the resource instead of
>>> forwarding it, if you have an example you can still give it for when it
>>> would be more performant/better.
>>> Thanks all,
>>> Greets
>>> Roy
>>>
>>>
>>>
>>>> On 29 Jul 2016, at 00:01, Roy Teeuwen <[email protected]> wrote:
>>>>
>>>> Also lastly to combine with my previous mail, it doesn’t have to be
>>>> only images. It could also be pdf’s, zips,… So the approach I am
>>>> using now won’t work in all the cases I want :)
>>>>
>>>>
>>>>> On 28 Jul 2016, at 23:44, Roy Teeuwen <[email protected]> wrote:
>>>>>
>>>>> Hey Jason, Olivier,
>>>>>
>>>>> @Jason:
>>>>> Damn, I feel so stupid now :D indeed, using
>>>>> /content/dam/nice-image.jpg.cdn./modification-date/20160815/nice-image.jpg
>>>>> fixes everything. I don’t have to implement the nonexistingservlet
>>>>> anymore ;), I was indeed thinking that jpg was the extension and so that
>>>>> the cdn HAD to be before the jpg because else the cdn would be the
>>>>> extension, but your explanation makes sense!
>>>>>
>>>>> @Olivier:
>>>>> "What's the reason for forwarding instead of reading/serving the resource
>>>>> from the repository in your current servlet?” Currently I am doing it
>>>>> like that, but I find the implementation a bit nasty… See how I did it:
>>>>>
>>>>> @Override
>>>>> protected void doGet(SlingHttpServletRequest request,
>>>>> SlingHttpServletResponse response) throws ServletException, IOException {
>>>>> Resource resource = request.getResource();
>>>>> final Image image = new Image(resource);
>>>>> image.set(Image.PN_REFERENCE, resource.getPath());
>>>>> try {
>>>>> final String mimeType = image.getMimeType();
>>>>> final Layer layer = image.getLayer(false, false, false);
>>>>> double quality = mimeType.equals(MIME_TYPE_GIF) ? 255 : 1.0;
>>>>> response.setContentType(mimeType);
>>>>> layer.write(mimeType, quality, response.getOutputStream());
>>>>> response.getOutputStream().flush();
>>>>> } catch (RepositoryException e) {
>>>>> LOG.error("could not get layer", e);
>>>>> }
>>>>> }
>>>>>
>>>>> Thats why I would think it would be easier / nicer to just forward it to
>>>>> the normal jpg handling servlet so that I don’t actually have to manually
>>>>> write it to the servlet response output. If there is a better way in
>>>>> serving the image resource from the repo, do tell :D.
>>>>>
>>>>> (For when you might be curious on why I am doing all of this, I am
>>>>> adding the latest modification date to all the image urls because
>>>>> the image urls actually get served by a cdn in front that proxies
>>>>> to the
>>>>> dam:
>>>>> https://some.azureedge.net/content/dam/nice-image.jpg.cdn./modifica
>>>>> ti on-date/20160815/nice-image.jpg , this way when the same image
>>>>> gets replaced in the dam by a new image with the same name on a
>>>>> page, I don’t have to invalidate the azure cdn cache because the
>>>>> modification date changing in the url takes care of that, and
>>>>> because I am not using a query string I also don’t have to care
>>>>> about nullifying the browser client-side caching)
>>>>>
>>>>> Thanks so far!
>>>>> Greets,
>>>>> Roy
>>>>>
>>>>>
>>>>>> On 28 Jul 2016, at 23:02, Jason Bailey <[email protected]> wrote:
>>>>>>
>>>>>> I was going to say that you shouldn't need to implement
>>>>>> NonExistingResource interface because a selector wouldn't impact
>>>>>> resolution then I had one of those blinding aha moments.
>>>>>>
>>>>>> In the case of the url you posted you aren't adding a selector, you are
>>>>>> modifying the resource name. '/content/dam/nice-image.jpg' does not
>>>>>> have an extension in the traditional sense, in the traditional sling
>>>>>> sense the resource would actually be '/content/dam/nice-image' and jpg
>>>>>> would be a handler that would return the asset as a jpeg.
>>>>>>
>>>>>> So when you are adding a selector to it it's not matching anything
>>>>>> because there is no '/content/dam/nice-image' resource to match against.
>>>>>>
>>>>>> So first make your request find the resource without the servlet in the
>>>>>> way. If you switch around the .cdn to the end and add a period at the
>>>>>> end of cdn like this...
>>>>>>
>>>>>> content/dam/nice-image.jpg.cdn./modification-date/20160815/nice-im
>>>>>> ag
>>>>>> e.jpg
>>>>>>
>>>>>> This should work to return the original nice-image.jpg at that point it
>>>>>> should be easier to create a servlet that will have the resource as part
>>>>>> of the request.
>>>>>>
>>>>>> Or optionally you could try adding a sling:resourceType to the image and
>>>>>> have a selector within that resource type that does what you need it do.
>>>>>>
>>>>>> -Jason
>>>>>>
>>>>>>
>>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Roy Teeuwen [mailto:[email protected]]
>>>>>> Sent: Thursday, July 28, 2016 3:17 PM
>>>>>> To: [email protected]
>>>>>> Subject: Re: Getting the actual resource from a request
>>>>>>
>>>>>> Hmm Sorry Jason,
>>>>>>
>>>>>> I might have had to notice that I am extending SlingSafeMethodServlet
>>>>>> but also implementing the following servlet:
>>>>>> https://docs.adobe.com/docs/en/cq/5-6-1/javadoc/com/day/cq/commons
>>>>>> /s ervlets/NonExistingResourceServlet.html
>>>>>>
>>>>>> And as it states in the docs itself, it also says:
>>>>>> public boolean accepts(SlingHttpServletRequest request) {
>>>>>> // get non-existing path (incl. selectors and extension!)
>>>>>> String path = request.getResource().getPath();
>>>>>> // return true if this servlet can handle this path
>>>>>> return true;
>>>>>> }
>>>>>>
>>>>>> So probably it’s not possible to do it through this interface in without
>>>>>> manually parseing :) It also says in the docs: Please note: This is a
>>>>>> temporary solution until Sling provides a built-in mechanism for this
>>>>>> use case. Not to be used by client implementations!
>>>>>>
>>>>>> Any clue if it is possible yet to do it with standard sling, the thing I
>>>>>> try to achieve is a servlet that also catches resources with a selector
>>>>>> AND suffix (as stated in my example of course) ?
>>>>>>
>>>>>> Thanks!
>>>>>> Roy
>>>>>>
>>>>>>
>>>>>>> On 28 Jul 2016, at 18:35, Jason Bailey <[email protected]> wrote:
>>>>>>>
>>>>>>> I'm under the understanding that it is;
>>>>>>>
>>>>>>> request.getResource().getPath()
>>>>>>>
>>>>>>> -----Original Message-----
>>>>>>> From: Roy Teeuwen [mailto:[email protected]]
>>>>>>> Sent: Thursday, July 28, 2016 12:31 PM
>>>>>>> To: [email protected]
>>>>>>> Subject: Getting the actual resource from a request
>>>>>>>
>>>>>>> Hey all,
>>>>>>>
>>>>>>> I am creating a SlingServlet that will work by both using a
>>>>>>> selector and a suffix. The resource is for example
>>>>>>> /content/dam/image.jpg and the actual url will be
>>>>>>> /content/dam/nice-image.cdn.jpg/modification-date/20160815/nice-image.
>>>>>>> jpg
>>>>>>>
>>>>>>> What is the most easy way to get the actual resource path again from
>>>>>>> the SlingHttpServletRequest? Currently I am doing the following but I
>>>>>>> find it a bit cumbersome:
>>>>>>>
>>>>>>> private String getResourcePath(SlingHttpServletRequest request) {
>>>>>>> String requestUrl =
>>>>>>> request.getRequestPathInfo().getResourcePath();
>>>>>>> int endIndex =
>>>>>>> requestUrl.lastIndexOf(request.getRequestPathInfo().getSuffix());
>>>>>>> String resourcePathWithSelector = requestUrl.substring(0,
>>>>>>> endIndex); endIndex =
>>>>>>> resourcePathWithSelector.lastIndexOf(request.getRequestPathInfo()
>>>>>>> .getSelectorString() + "." +
>>>>>>> request.getRequestPathInfo().getExtension());
>>>>>>> return resourcePathWithSelector.substring(0, endIndex) +
>>>>>>> request.getRequestPathInfo().getExtension();
>>>>>>> }
>>>>>>>
>>>>>>> Is there an easier way or is parsing it like this the only way?
>>>>>>>
>>>>>>> Also after I got the actual resourcePath, I tried doing the following,
>>>>>>> but this doesn’t seem to work, any clue on why?
>>>>>>>
>>>>>>> @Override
>>>>>>> protected void doGet(SlingHttpServletRequest request,
>>>>>>> SlingHttpServletResponse response) throws ServletException, IOException
>>>>>>> {
>>>>>>> RequestDispatcherOptions opts = new RequestDispatcherOptions();
>>>>>>> opts.setReplaceSelectors("");
>>>>>>> String resourcePath = getResourcePath(request);
>>>>>>> RequestDispatcher dispatcher =
>>>>>>> request.getRequestDispatcher(resourcePath, opts);
>>>>>>> if (dispatcher != null) {
>>>>>>> dispatcher.forward(request, response);
>>>>>>> }
>>>>>>> }
>>>>>>>
>>>>>>> I would expect that the previous would actually just forward it
>>>>>>> to the actual image being fetched from the getResourcePath but it
>>>>>>> just gives me a 404 not found (I checked the getResourcePath and
>>>>>>> it does return
>>>>>>> /content/dam/nice-image.jpg)
>>>>>>>
>>>>>>> Thanks!
>>>>>>> Roy
>>>>>>
>>>>>
>>>>
>>>
>>
>