View Javadoc

1   package com.atlassian.seraph.service;
2   
3   import com.atlassian.seraph.util.PathMapper;
4   import com.atlassian.seraph.SecurityService;
5   import com.atlassian.seraph.config.SecurityConfig;
6   import com.atlassian.seraph.util.XMLUtils;
7   import com.atlassian.seraph.util.CachedPathMapper;
8   import com.opensymphony.util.ClassLoaderUtil;
9   import org.apache.log4j.Category;
10  import org.apache.commons.collections.LRUMap;
11  import org.w3c.dom.Element;
12  import org.w3c.dom.NodeList;
13  
14  import javax.servlet.http.HttpServletRequest;
15  import javax.xml.parsers.DocumentBuilderFactory;
16  import java.net.URL;
17  import java.util.*;
18  
19  /**
20   * Configures Seraph to require certain roles to access certain URL paths.
21   * <p>
22   * Single init-param 'config.file' which is the location of the XML config file.
23   * Default value is '/seraph-paths.xml' (loaded from classpath - usually in /WEB-INF/classes)
24   * <p>
25   * Here's a sample of the XML config file. Path names must be unique
26   *<p>
27   * <pre>
28   * &lt;seraph-paths>
29   *	&lt;path name="admin">
30   * 		&lt;url-pattern>/secure/admin/*&lt;/url-pattern>
31   * 		&lt;role-name>administrators&lt;/role-name>
32   * 	&lt;/path>
33   * 	&lt;path name="secured">
34   *		&lt;url-pattern>/secure/*&lt;/url-pattern>
35   *		&lt;role-name>users&lt;/role-name>
36   *	&lt;/path>
37   * &lt;/seraph-paths>
38   * </pre>
39   *
40   */
41  public class PathService implements SecurityService
42  {
43      private static final Category log = Category.getInstance(PathService.class);
44      static String CONFIG_FILE_PARAM_KEY = "config.file";
45  
46      String configFileLocation = "seraph-paths.xml";
47  
48      // maps url patterns to path names
49      private PathMapper pathMapper = new CachedPathMapper(new LRUMap(10), new LRUMap(10));
50  
51      // maps roles to path names
52      private Map paths = new HashMap();
53  
54  
55      /**
56       * Init the service - configure it from the config file
57       */
58      public void init(Map params, SecurityConfig config)
59      {
60          try
61          {
62              if (params.get(CONFIG_FILE_PARAM_KEY) != null)
63              {
64                  configFileLocation = (String) params.get(CONFIG_FILE_PARAM_KEY);
65              }
66  
67              configurePathMapper();
68          }
69          catch (Exception e)
70          {
71              e.printStackTrace();
72          }
73      }
74  
75  
76      /**
77       * Configure the path mapper
78       */
79      private void configurePathMapper()
80      {
81          try
82          {
83              DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
84              URL fileUrl = ClassLoaderUtil.getResource(configFileLocation, this.getClass());
85  
86              if (fileUrl == null)
87                  fileUrl = ClassLoaderUtil.getResource("/" + configFileLocation, this.getClass());
88  
89  
90              if (fileUrl == null)
91                  return;
92  
93              // Parse document
94              org.w3c.dom.Document doc = factory.newDocumentBuilder().parse(fileUrl.toString());
95  
96              // Get list of actions
97              Element root = doc.getDocumentElement();
98              NodeList pathNodes = root.getElementsByTagName("path");
99  
100             // Build list of views
101             for (int i = 0; i < pathNodes.getLength(); i++)
102             {
103                 Element path = (Element) pathNodes.item(i);
104 
105                 String pathName = path.getAttribute("name");
106                 String roleNames = XMLUtils.getContainedText(path, "role-name");
107                 String urlPattern = XMLUtils.getContainedText(path, "url-pattern");
108 
109                 if (roleNames != null && urlPattern != null)
110                 {
111                     String[] rolesArr = parseRoles(roleNames);
112                     paths.put(pathName, rolesArr);
113                     pathMapper.put(pathName, urlPattern);
114                 }
115             }
116         }
117         catch (Exception ex)
118         {
119             log.error(ex);
120         }
121     }
122 
123     protected String[] parseRoles(String roleNames) {
124         StringTokenizer st = new StringTokenizer(roleNames, ",; \t\n", false);
125         String[] roles = new String[st.countTokens()];
126         int i = 0;
127         while (st.hasMoreTokens()) {
128             roles[i] = st.nextToken();
129             i++;
130         }
131         return roles;
132     }
133 
134     public void destroy()
135     {
136     }
137 
138     public Set getRequiredRoles(HttpServletRequest request)
139     {
140         String servletPath = request.getServletPath();
141         return getRequiredRoles(servletPath);
142     }
143 
144     public Set getRequiredRoles(String servletPath)
145     {
146         Set requiredRoles = new HashSet();
147 
148         // then check path mappings first and add any required roles
149         Collection constraintMatches = pathMapper.getAll(servletPath);
150 
151         if (constraintMatches == null) throw new RuntimeException("No constraints matched for path "+servletPath);
152         for (Iterator iterator = constraintMatches.iterator(); iterator.hasNext();)
153         {
154             String constraint = (String) iterator.next();
155 
156             String[] rolesForPath = (String[]) paths.get(constraint);
157             for (int i = 0; i < rolesForPath.length; i++)
158             {
159                 if (!requiredRoles.contains(rolesForPath[i])) // since requiredRoles is a set, isn't this useless?
160                 {
161                     requiredRoles.add(rolesForPath[i]);
162                 }
163             }
164         }
165 
166         return requiredRoles;
167     }
168 }