Expand my Community achievements bar.

SOLVED

Protecting new fields to userEditor (stored at ./profile/...) for user administration

Avatar

Level 5

A app that I'm working on requires a couple of additional fields be added to a user's profile:

  • ./profile/approvers (used to programmatically construct an approver group for use in workflows)
  • ./profile/contactReassign, ./profile/authorReassign -- used to change news articles listing the user as the author or contact to another user after they have departed the organization (or some other event requiring such a reassignment)

I have the field added to the UI alright, and the data successfully saves, but I need to restrict the ability to edit these fields to members of the group "newsadmins." Since a user may edit their own profile node, I need to edit the request to exclude the above parameters.

[img]http://i.imgur.com/kUjXdFk.png[/img]

I will be hiding these fields from non-newsadmins, that's no problem, but this might still leave me open to attack if the user is smart enough to modify the dom before submitting the form. 

Obviosuly a user could just run a curl command: 

curl -u aaron.mcdonald@mailinator.com:password -X POST --data "./profile/approvers=someone" http://localhost:4502/home/users/geometrixx-outdoors/aaron.mcdonald@mailinator.com.rw.userprops.html

My initial thought was to create a filter servlet and then somehow modify the submitted parameters:

@Component(immediate=true, metatype=false) @Service(value=javax.servlet.Filter.class) @Properties({ @Property(name="service.pid", value="org.uc.news.core.filters.UserEditorFilter",propertyPrivate=false), @Property(name="service.description",value="User Editor Filter", propertyPrivate=false), @Property(name="filter.scope",value="request", propertyPrivate=false), @Property(name="service.ranking",intValue = 20, propertyPrivate=false), @Property(name="servlet.filter.patter",value=".*rw\\.userprops\\.html$", propertyPrivate=false) }) public class UserEditorFilter implements Filter { ... }

The filter gets registered and called on the request, but I'm running into a wall when I come to figuring out how to actually get at the parameters and remove them.

So far, I've tried using the method of wrapping the request before I pass it on described here:  http://stackoverflow.com/questions/1413129/modify-request-parameter-with-servlet-filter 

 

//in doFilter():

if(!newsAdmin) { logger.info("using filtered request"); filterChain.doFilter(new FilteredRequest(request), response); return; } ... static class FilteredRequest extends HttpServletRequestWrapper { private final Logger logger = LoggerFactory.getLogger(getClass()); public FilteredRequest(ServletRequest request) { super((HttpServletRequest)request); } @Override public Map getParameterMap() { Map params = super.getParameterMap(); try { params.remove("./profile/approvers"); } catch (Exception e) { logger.info("no approver to remove"); } try { params.remove("./profile/contactReassign"); } catch (Exception e) { logger.info("no contact reassign to remove"); } try { params.remove("./profile/authorReassign"); } catch (Exception e) { logger.info("no contact reassign to remove"); } return params; } }

My FilteredRequest is called initialized and passed down the chain, however, getParameterMap() never seems to get called--so those parameters aren't removed, and the parameters end up being saved to the node like normal.
 

@Component(immediate=true, metatype=false) @Service(value=javax.servlet.Filter.class) @Properties({ @Property(name="service.pid", value="org.uc.news.core.filters.UserEditorFilter",propertyPrivate=false), @Property(name="service.description",value="User Editor Filter", propertyPrivate=false), @Property(name="filter.scope",value="request", propertyPrivate=false), @Property(name="service.ranking",intValue = 20, propertyPrivate=false), @Property(name="servlet.filter.patter",value=".*rw\\.userprops\\.html$", propertyPrivate=false) }) public class UserEditorFilter implements Filter { ... }

This more or less works how I want it to. I'm running into a wall when I come to figuring out how to actually get at the parameters and remove them.

So far, I've tried using the method of wrapping the request before I pass it on described here:  http://stackoverflow.com/questions/1413129/modify-request-parameter-with-servlet-filter 

 

if(!newsAdmin) { logger.info("useing filtered request"); filterChain.doFilter(new FilteredRequest(request), response); return; } ... static class FilteredRequest extends HttpServletRequestWrapper { private final Logger logger = LoggerFactory.getLogger(getClass()); public FilteredRequest(ServletRequest request) { super((HttpServletRequest)request); } @Override public Map getParameterMap() { Map params = super.getParameterMap(); try { params.remove("./profile/approvers"); } catch (Exception e) { logger.info("no approver to remove"); } try { params.remove("./profile/contactReassign"); } catch (Exception e) { logger.info("no contact reassign to remove"); } try { params.remove("./profile/authorReassign"); } catch (Exception e) { logger.info("no contact reassign to remove"); } return params; } }

My FilteredRequest is called initialized and passed down the chain, however, getParameterMap() never seems to get called--so those parameters aren't removed, and the parameters end up being saved to the node like normal.

Perhaps im going about this the wrong way, or maybe I need to override another method in the wrapper. Any help is appreciated.

1 Accepted Solution

Avatar

Correct answer by
Level 5

For what it's worth, I ended up going to a differnet route with this.

I do not think you are able to change that paramters, the method called is actually getRequestParamterMap() in SlingHttpServletRequest. Attempting to wrap this and @Override that method were futile.

Instead, I just throw a servlet error if the user doesn't have permission. The solution is included in full below:

package org.uc.news.core.filters; import java.io.IOException; import java.util.Iterator; import java.util.Map; import javax.jcr.Session; 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.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SlingFilter( label = "User Editor Filter", description = "Manipulates the saving of user profile properties to ensure that non-newsadmins do not attempt to edit them", metatype = false, generateComponent = true, // True if you want to leverage activate/deactivate or manage its OSGi life-cycle generateService = true, // True; required for Sling Filters order = 50001, // The smaller the number, the earlier in the Filter chain (can go negative); // Defaults to Integer.MAX_VALUE which push it at the end of the chain scope = SlingFilterScope.REQUEST) // REQUEST, INCLUDE, FORWARD, ERROR, COMPONENT (REQUEST, INCLUDE, COMPONENT) @Properties({ @Property(name="service.pid", value="org.uc.news.core.filters.UserEditorFilter",propertyPrivate=false), @Property(name="service.vendor",value="University of Cincinnati", propertyPrivate=false) }) public class UserEditorFilter implements Filter { private final Logger log = LoggerFactory.getLogger(UserEditorFilter.class); @Reference private ResourceResolverFactory resolverFactory; public static final String[] dangerousParameters = { "./profile/approvers", "./profile/authorReassign", "./profile/contactReassign" }; @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { //see if this request contains ./profile/approvers, ./profile/contactReassign, ./profile/authorReassign Boolean stopRequest = false; Map pm = request.getParameterMap(); for(String name: dangerousParameters) { if(pm.containsKey(name)) { stopRequest = true; } } if(stopRequest) { //see if they are in newsadmins try { ResourceResolver resourceResolver = resolverFactory.getAdministrativeResourceResolver(null); UserManager userManager = resourceResolver.adaptTo(UserManager.class); Session session = resourceResolver.adaptTo(Session.class); Iterator<Group> ig = userManager.getAuthorizable(((SlingHttpServletRequest)request).getResource().getResourceResolver().adaptTo(Session.class).getUserID()).memberOf(); for(;ig.hasNext();) { String gid = ig.next().getID(); if(gid.equals("newsadmin")) stopRequest = false; } } catch (Exception e) { } } if(stopRequest) { throw(new ServletException("No rights to edit a user's news profile")); } else { //normal case without the profile params or they are admin filterChain.doFilter(request,response); } } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } }

View solution in original post

2 Replies

Avatar

Correct answer by
Level 5

For what it's worth, I ended up going to a differnet route with this.

I do not think you are able to change that paramters, the method called is actually getRequestParamterMap() in SlingHttpServletRequest. Attempting to wrap this and @Override that method were futile.

Instead, I just throw a servlet error if the user doesn't have permission. The solution is included in full below:

package org.uc.news.core.filters; import java.io.IOException; import java.util.Iterator; import java.util.Map; import javax.jcr.Session; 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.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SlingFilter( label = "User Editor Filter", description = "Manipulates the saving of user profile properties to ensure that non-newsadmins do not attempt to edit them", metatype = false, generateComponent = true, // True if you want to leverage activate/deactivate or manage its OSGi life-cycle generateService = true, // True; required for Sling Filters order = 50001, // The smaller the number, the earlier in the Filter chain (can go negative); // Defaults to Integer.MAX_VALUE which push it at the end of the chain scope = SlingFilterScope.REQUEST) // REQUEST, INCLUDE, FORWARD, ERROR, COMPONENT (REQUEST, INCLUDE, COMPONENT) @Properties({ @Property(name="service.pid", value="org.uc.news.core.filters.UserEditorFilter",propertyPrivate=false), @Property(name="service.vendor",value="University of Cincinnati", propertyPrivate=false) }) public class UserEditorFilter implements Filter { private final Logger log = LoggerFactory.getLogger(UserEditorFilter.class); @Reference private ResourceResolverFactory resolverFactory; public static final String[] dangerousParameters = { "./profile/approvers", "./profile/authorReassign", "./profile/contactReassign" }; @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { //see if this request contains ./profile/approvers, ./profile/contactReassign, ./profile/authorReassign Boolean stopRequest = false; Map pm = request.getParameterMap(); for(String name: dangerousParameters) { if(pm.containsKey(name)) { stopRequest = true; } } if(stopRequest) { //see if they are in newsadmins try { ResourceResolver resourceResolver = resolverFactory.getAdministrativeResourceResolver(null); UserManager userManager = resourceResolver.adaptTo(UserManager.class); Session session = resourceResolver.adaptTo(Session.class); Iterator<Group> ig = userManager.getAuthorizable(((SlingHttpServletRequest)request).getResource().getResourceResolver().adaptTo(Session.class).getUserID()).memberOf(); for(;ig.hasNext();) { String gid = ig.next().getID(); if(gid.equals("newsadmin")) stopRequest = false; } } catch (Exception e) { } } if(stopRequest) { throw(new ServletException("No rights to edit a user's news profile")); } else { //normal case without the profile params or they are admin filterChain.doFilter(request,response); } } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } }

Avatar

Level 10

This is helpful - thank you for posting this.