Expand my Community achievements bar.

Guidelines for the Responsible Use of Generative AI in the Experience Cloud Community.

403 Forbidden error on get servlet

Avatar

Level 5

I'm testing a servlet I use to wait for get requests to the createpagewizard.html at a given path that redirects the requests to another directory (it's a news system, so I want new articles to always go in the appropriate year/month path). It works for the specified paths, but on paths higher int he tree, I get a 403 error:

Forbidden Cannot serve request to /mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news/ in org.apache.sling.servlets.get.DefaultGetServlet Request Progress: 0 TIMER_START{Request Processing} 0 COMMENT timer_end format is {<elapsed msec>,<timer name>} <optional message> 0 LOG Method=GET, PathInfo=/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news/ 0 TIMER_START{ResourceResolution} 0 TIMER_END{0,ResourceResolution} URI=/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news/ resolves to Resource=MergedResource [path=/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news, resources=[Ljava.lang.String;@550aeead] 0 LOG Resource Path Info: SlingRequestPathInfo: path='/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news', selectorString='null', extension='null', suffix='/' 0 TIMER_START{ServletResolution} 0 TIMER_START{resolveServlet(/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news)} 0 TIMER_END{0,resolveServlet(/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news)} Using servlet org.apache.sling.servlets.get.DefaultGetServlet 0 TIMER_END{0,ServletResolution} URI=/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news/ handled by Servlet=org.apache.sling.servlets.get.DefaultGetServlet 0 LOG Applying Requestfilters 0 LOG Calling filter: com.adobe.granite.resourceresolverhelper.impl.ResourceResolverHelperImpl 0 LOG Calling filter: org.apache.sling.bgservlets.impl.BackgroundServletStarterFilter 0 LOG Calling filter: org.uc.news.core.filters.UserEditorFilter 0 LOG Calling filter: com.adobe.granite.rest.impl.servlet.ApiResourceFilter 0 LOG Calling filter: org.apache.sling.i18n.impl.I18NFilter 0 LOG Calling filter: com.adobe.granite.httpcache.impl.InnerCacheFilter 0 LOG Calling filter: org.apache.sling.rewriter.impl.RewriterFilter 0 LOG Calling filter: com.adobe.cq.mcm.campaign.servlets.CampaignCopyTracker 0 LOG Calling filter: com.day.cq.wcm.core.impl.WCMRequestFilter 0 LOG Calling filter: com.adobe.cq.history.impl.HistoryRequestFilter 0 LOG Calling filter: com.adobe.granite.optout.impl.OptOutFilter 0 LOG Calling filter: com.day.cq.wcm.foundation.forms.impl.FormsHandlingServlet 0 LOG Calling filter: com.adobe.cq.social.commons.cors.CORSAuthenticationFilter 0 LOG Calling filter: com.day.cq.analytics.provisioning.impl.UserAuthenticationRequestFilter 0 LOG Calling filter: org.apache.sling.engine.impl.debug.RequestProgressTrackerLogFilter 0 LOG Calling filter: com.day.cq.wcm.mobile.core.impl.redirect.RedirectFilter 0 LOG RedirectFilter did not redirect (not redirecting in author mode) 0 LOG Calling filter: com.day.cq.wcm.core.impl.warp.TimeWarpFilter 0 LOG Calling filter: com.day.cq.wcm.core.impl.AuthoringUIModeServiceImpl 0 LOG Calling filter: org.apache.sling.security.impl.ContentDispositionFilter 0 LOG Calling filter: com.adobe.granite.csrf.impl.CSRFFilter 0 LOG Calling filter: com.adobe.granite.requests.logging.impl.RequestLoggerImpl 0 LOG Calling filter: com.day.cq.dam.core.impl.servlet.ActivityRecordHandler 0 LOG Calling filter: com.day.cq.dam.core.impl.assetlinkshare.AdhocAssetShareAuthHandler 0 LOG Calling filter: com.adobe.cq.social.ugcbase.security.impl.SaferSlingPostServlet 0 LOG Applying Componentfilters 0 LOG Calling filter: com.day.cq.wcm.core.impl.WCMComponentFilter 1 LOG Calling filter: com.day.cq.wcm.core.impl.WCMDebugFilter 1 LOG Calling filter: com.day.cq.personalization.impl.TargetComponentFilter 1 TIMER_START{org.apache.sling.servlets.get.DefaultGetServlet#0} 1 LOG Using org.apache.sling.servlets.get.impl.helpers.StreamRendererServlet to render for extension=null 1 LOG Applying Error filters 1 LOG Calling filter: org.apache.sling.i18n.impl.I18NFilter 1 LOG Calling filter: org.apache.sling.rewriter.impl.RewriterFilter 1 TIMER_START{handleError:status=403} 1 TIMER_END{0,handleError:status=403} Using handler /libs/sling/servlet/errorhandler/default.jsp 2 LOG Found processor for post processing ProcessorConfiguration: {contentTypes=[text/html], order=-1, active=true, valid=true, processErrorResponse=true, pipeline=(generator=Config(type=htmlparser, config={}), transformers=(Config(type=linkchecker, config={}), Config(type=mobile, config=JcrPropertyMap [node=Node[NodeDelegate{tree=/libs/cq/config/rewriter/default/transformer-mobile: { jcr:primaryType = nt:unstructured, component-optional = true}}], values={jcr:primaryType=nt:unstructured, component-optional=true}]), Config(type=mobiledebug, config=JcrPropertyMap [node=Node[NodeDelegate{tree=/libs/cq/config/rewriter/default/transformer-mobiledebug: { jcr:primaryType = nt:unstructured, component-optional = true}}], values={jcr:primaryType=nt:unstructured, component-optional=true}]), Config(type=contentsync, config=JcrPropertyMap [node=Node[NodeDelegate{tree=/libs/cq/config/rewriter/default/transformer-contentsync: { jcr:primaryType = nt:unstructured, component-optional = true}}], values={jcr:primaryType=nt:unstructured, component-optional=true}]), serializer=Config(type=htmlwriter, config={}))} 3 TIMER_END{3,Request Processing} Dumping SlingRequestProgressTracker Entries

I've seen reports of this with POST servlets where it was a CSRF issue, but not GET servlet. Nothing comes up in my error logs. I've included the whole source for the servlet below (minus imports to come within length constraints):

//TODO regex like [0-9]{4} instead of the year hardcoded? @Component(metatype = true) @Service(Servlet.class) @Properties({ @Property(name = "sling.servlet.methods", value = "GET"), @Property(name = "sling.servlet.paths", value = { "/libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles", "/libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles/2016" }) }) public class CreationServlet extends SlingSafeMethodsServlet { private static final long serialVersionUID = 3044360007999929023L; private static final Logger log = LoggerFactory.getLogger(CreationServlet.class); @Reference private ResourceResolverFactory resolverFactory; @Override protected void doGet(final SlingHttpServletRequest req, final SlingHttpServletResponse resp) throws ServletException, IOException { try { final ResourceResolver rr = resolverFactory.getAdministrativeResourceResolver(null); final PageManager pm = rr.adaptTo(PageManager.class); boolean hasYearPart = false; String[] path = req.getPathInfo().split("/"); if(path[path.length-1].length() == 4) hasYearPart = true; // check if there is a page at /content/uc/news/articles/YYYY/MM // (for the current year and month), create them if not, then // redirect to a page creation under there Date d = new Date(); SimpleDateFormat ysdf = new SimpleDateFormat("yyyy"); SimpleDateFormat msdf = new SimpleDateFormat("MM"); String yearpart = ysdf.format(d); // gets something like 2016 String monthpart = msdf.format(d); // gets something like 01 String baseurl = "/content/uc/news/articles"; String template = "/conf/news/settings/wcm/templates/news-portal"; Resource articleRes = rr.resolve(baseurl); Page yearPage = null; Page monthPage = null; if(articleRes.getChild(yearpart) != null) yearPage = articleRes.getChild(yearpart).adaptTo(Page.class); if (yearPage == null) { yearPage = pm.create(baseurl, yearpart, template, yearpart); rr.commit(); } if(articleRes.getChild(yearpart) != null && articleRes.getChild(yearpart).getChild(monthpart) != null) monthPage = articleRes.getChild(yearpart).getChild(monthpart).adaptTo(Page.class); if (monthPage == null) { monthPage = pm.create(baseurl + "/" + yearpart, monthpart, template, monthpart); rr.commit(); } if(hasYearPart) resp.sendRedirect(yearpart + "/" + monthpart); else resp.sendRedirect("articles/" + yearpart + "/" + monthpart); } catch (Exception e) { resp.getWriter().println(e); for(StackTraceElement ln : e.getStackTrace()) resp.getWriter().println(ln); } } }

The only thing I can think is happening is something with the lowerlevel funtionality of the servlet resolver, but the note from the sling documentation: "A servlet using this property might be ignored unless its path is included in the Execution Paths (servletresolver.paths) configuration setting of the SlingServletResolver service." doesn't seem to apply in this case. I'm using a servler elsewhere at /bin/news/articles (and /bin/news/articles.rss and .json) with no issues--perhaps this is a problem because there is a servlet listening at  /mnt/overlay/wcm/core/content/sites/createpagewizard.html already?

Any help is appreciated. Thanks

13 Replies

Avatar

Level 8

You have your servlet restricted to 2 paths, so the path that you're trying to use will be a 403 because you haven't allowed access to it.

@Property(name = "sling.servlet.paths", value = { "/libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles", "/libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles/2016" })

If you want to add access to the location you're trying, modify to this

@Property(name = "sling.servlet.paths", value = { "/libs/wcm/core/content/sites/createpagewizard.html/content/uc/news" })

And that covers all of the paths.

Avatar

Level 5

Sorry, perhaps I wasn't being clear.

I only want the srevet to run on those 2 paths, everything else should fall back to whatever other servlet was running on the default for the page creation wizard.

I make plenty of there Get requests (inclusing creating pages under /content/geometrix...) for example that are served up just fine, so I'm not really clear on what's happening for this set of paths

Avatar

Level 7

I agree with leeasling,

But if you want to access your servlet by those two paths only then thats fine, its only that by the logs you have attached it seems you are trying to access it from a parent path which is not mapped anywhere in OSGI to any servlet. Thus you are getting this exception. Not sure if this was the answer you were looking for though.

Thanks

Tuhin

Avatar

Level 5

(edit: not sure why half of this is green)


Again, sorry for not being clear in my inital post. Let me try to explain it a different way

All I want my servlet to do is capture get requests to

  • /libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles
  • /libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles/2016

and redirect them to /libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles/2016/06 (or whatever the current month is). This will effectively make so that a regular user can't create a new page in the /articles or /articles/2016 folder (we have upwards of 100 stories per month, so I want to keep a solid content hierarchy).

Still, I want to retain the ability to create pages at the /content/uc/news (or /cotnent/uc for that matter) level like normal. In these cases, the servlet appears to be triggering where it's not supposed to. It's not configured for paths like /libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/  my understanding of sling servlets is that my CreationServlet shouldn't be called at all in that case and it should fall back to the standard creation servlet; documentation for the sling.servlet.paths: "A list of absolute paths under which the servlet is accessible as a Resource."

Note that, for example, the servlet does not get triggered at /libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles/2016/06, since that doesn't match the sling.servlet.paths property.

I'm beginning to think this may be a bug in sling.

In summary:

  • /libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles -> redirect to "/libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles/2016/06 (successful)
  • /libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles/2016 -> redirect to "/libs/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles/2016/06 (successful)
  • /libs/wcm/core/content/sites/createpagewizard.html/content/uc, no redirect (403 forbidden) (unexpected behavior)
  • /libs/wcm/core/content/sites/createpagewizard.html/content/uc/news, no redirect (403 forbidden) (unexpected behavior)
  • /libs/wcm/core/content/sites/createpagewizard.html/content/geometrix, no redirect (200 success) (expected behavior)
  • /libs/wcm/core/content/sites/createpagewizard.html/content/uc/articles/2016/06, no redirect (200 success) (expected behavior)

Avatar

Level 7

I think in this scenario you should use a wild card to achieve this functionality.

try adding /content/uc/* to the path and check.

Thanks

Tuhin

Avatar

Level 5

That's unfortunately not going to achieve what I want:

"Still, I want to retain the ability to create pages at the /content/uc/news (or /cotnent/uc for that matter) level like normal. In these cases, the servlet appears to be triggering where it's not supposed to."

The servlet should only trigger on the pages I originally specified. The system should be have like normal (like it would in the absence of my servlet--no 403 and no redirecting) on any other path.

Avatar

Level 8

I understand what your request is.
Does the requesting user have permission to read/write /content/uc and /content/uc/news?

Avatar

Level 8

Also, one thing to try out to see if the response that 403's makes it to your servlet, start AEM in debug mode locally (java -jar cq5-author-4502.jar -debug 30303) and attach a remote debugger and see if any of your code is getting executed.

Avatar

Level 5

"Does the requesting user have permission to read/write /content/uc and /content/uc/news?"

Yes, I'm working as admin.

In the absense of this servlet, clicking "Create->Page" will take you to the page creation wizard (the out of the box wizard: http://localhost:4502/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/) with the request suffix as the pageParent. All this servlet is supposed to do is listen for requests with the suffix /content/uc/news/articles and ...article/2016 and redirect so that suffix reads something like /content/uc/news/articles/2016/06. For all pages not at /content/uc/news/articles/ or ...articles/2016 it should behave as is does out of the box.

However, given the trouble, I think I'm going to go another route, creating a "News Page" wizard that will handle some of the logic on the front end (not ideal) and no have to rely on servlets to do this redirecting.

Thanks for the help

Avatar

Level 8

I think a better approach may be to use a filter rather than a servlet.  See this code example and this article  I'm thinking what you can do is capture the request with the filter, and then redirect to the path you want with the year/month.

Avatar

Level 5

You're absolutely right-it should definitely be a filter. Here's the code I landed on"

package org.uc.news.core.filters; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.sling.SlingFilter; import org.apache.felix.scr.annotations.sling.SlingFilterScope; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.PageManager; @SlingFilter( label = "News Page Creation Filter", description = "Redirects attempts to create a page to the current month's page under /content/uc/news/articles/[current year]", metatype = true, generateComponent = true, // True if you want to leverage activate/deactivate or manage its OSGi life-cycle generateService = true, // True; required for Sling Filters order = Integer.MAX_VALUE, scope = SlingFilterScope.REQUEST) // REQUEST, INCLUDE, FORWARD, ERROR, COMPONENT (REQUEST, INCLUDE, COMPONENT) @Properties({ @Property(name="service.pid", value="org.uc.news.core.filters.Creationfilter",propertyPrivate=false), @Property(name="service.vendor",value="University of Cincinnati", propertyPrivate=false), @Property(name="sling.filter.pattern",value= "/mnt/overlay/wcm/core/content/sites/createpagewizard.html/content/uc/news/articles(/[0-9]{4})?", propertyPrivate=false), }) public class CreationFilter implements Filter { private static final long serialVersionUID = 3044360007999929023L; private static final Logger log = LoggerFactory.getLogger(CreationFilter.class); @Reference private ResourceResolverFactory resolverFactory; @Override public void init(FilterConfig filterConfig) throws ServletException { // Usually, do nothing } @Override public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { SlingHttpServletRequest req = (SlingHttpServletRequest) request; SlingHttpServletResponse resp = (SlingHttpServletResponse) response; try { final ResourceResolver rr = resolverFactory.getAdministrativeResourceResolver(null); final PageManager pm = rr.adaptTo(PageManager.class); boolean hasYearPart = false; String[] path = req.getPathInfo().split("/"); if(path[path.length-1].length() == 4) hasYearPart = true; // check if there is a page at /content/uc/news/articles/YYYY/MM // (for the current year and month), create them if not, then // redirect to a page creation under there Date d = new Date(); SimpleDateFormat ysdf = new SimpleDateFormat("yyyy"); SimpleDateFormat msdf = new SimpleDateFormat("MM"); String yearpart = ysdf.format(d); // gets something like 2016 String monthpart = msdf.format(d); // gets something like 01 String baseurl = "/content/uc/news/articles"; String template = "/conf/news/settings/wcm/templates/news-portal"; Resource articleRes = rr.resolve(baseurl); Page yearPage = null; Page monthPage = null; if(articleRes.getChild(yearpart) != null) yearPage = articleRes.getChild(yearpart).adaptTo(Page.class); if (yearPage == null) { yearPage = pm.create(baseurl, yearpart, template, yearpart); rr.commit(); } if(articleRes.getChild(yearpart) != null && articleRes.getChild(yearpart).getChild(monthpart) != null) monthPage = articleRes.getChild(yearpart).getChild(monthpart).adaptTo(Page.class); if (monthPage == null) { monthPage = pm.create(baseurl + "/" + yearpart, monthpart, template, monthpart); rr.commit(); } if(hasYearPart) resp.sendRedirect(yearpart + "/" + monthpart); else resp.sendRedirect("articles/" + yearpart + "/" + monthpart); } catch (Exception e) { resp.getWriter().println(e); for(StackTraceElement ln : e.getStackTrace()) resp.getWriter().println(ln); } } @Override public void destroy() { // TODO Auto-generated method stub } }

Avatar

Level 8

The only other thing I would change is that you're using the resource resolver factory to create another resource resolver.  You already have a resource resolver on the SlingHttpServletRequest (request.getResourceResolver()) - if you use this resource resolver, it will ensure that only people with the appropriate permissions can create articles.

If you insist on using an administrative resource resolver, i highly recommend adding a "finally" clause to your try/catch and close the resource resolver.

Avatar

Level 5

Thanks--I can use the resolver on the request, since users in my authors group will have creation rights under /articles.