1 package com.atlassian.seraph.service;
2
3 import com.atlassian.seraph.util.PathMapper;
4 import com.atlassian.seraph.util.CachedPathMapper;
5 import com.atlassian.seraph.SecurityService;
6 import com.atlassian.seraph.config.SecurityConfig;
7 import com.opensymphony.util.ClassLoaderUtil;
8 import org.apache.log4j.Category;
9 import org.apache.commons.collections.map.LRUMap;
10 import org.w3c.dom.Element;
11 import org.w3c.dom.NodeList;
12 import org.xml.sax.SAXException;
13
14 import javax.servlet.http.HttpServletRequest;
15 import javax.xml.parsers.DocumentBuilderFactory;
16 import javax.xml.parsers.ParserConfigurationException;
17 import java.net.URL;
18 import java.util.*;
19 import java.io.IOException;
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public class WebworkService implements SecurityService
46 {
47 private static final Category log = Category.getInstance(WebworkService.class);
48 private static final String ROLES_REQUIRED_ATTR = "roles-required";
49 private static final String ACTION_EXTENSION_INIT_PARAM = "action.extension";
50 private static final String ACTIONS_XML_FILE_INIT_PARAM = "actions.xml.file";
51
52
53 private static final String ACTION_EXTENSION_DEFAULT = "action";
54
55
56 private static final String ACTIONS_XML_FILE_DEFAULT = "actions";
57
58
59
60
61
62 private PathMapper actionMapper = new CachedPathMapper(new LRUMap(500), new LRUMap(10));
63
64
65 private Map rolesMap = new HashMap();
66
67 public void init(Map params, SecurityConfig config)
68 {
69 try
70 {
71
72 String extension = (String) params.get(ACTION_EXTENSION_INIT_PARAM);
73 if (extension == null) extension = ACTION_EXTENSION_DEFAULT;
74
75
76
77 String actionsXmlFile = (String) params.get(ACTIONS_XML_FILE_INIT_PARAM);
78 if (actionsXmlFile == null) actionsXmlFile = ACTIONS_XML_FILE_DEFAULT;
79
80 configureActionMapper(extension, actionsXmlFile);
81 }
82 catch (RuntimeException e)
83 {
84 log.error("Failed to initialise WebworkService", e);
85 }
86 }
87
88 private void configureActionMapper(String extension, String actionsXmlFile)
89 {
90 org.w3c.dom.Document doc = parseActionsXmlFile(actionsXmlFile);
91
92
93 NodeList actions = doc.getElementsByTagName("action");
94
95 String rootRolesRequired = overrideRoles(null, doc.getDocumentElement());
96
97
98 for (int i = 0; i < actions.getLength(); i++)
99 {
100 Element action = (Element) actions.item(i);
101 String actionName = action.getAttribute("name");
102 String actionAlias = action.getAttribute("alias");
103 final String actionRolesRequired = overrideRoles(rootRolesRequired, action);
104
105 if (actionRolesRequired != null)
106 {
107
108 if (actionAlias != null)
109 {
110 actionMapper.put(actionAlias, "/" + actionAlias + "." + extension);
111 rolesMap.put(actionAlias, actionRolesRequired);
112 actionMapper.put(actionAlias + "!*", "/" + actionAlias + "!*." + extension);
113 rolesMap.put(actionAlias + "!*", actionRolesRequired);
114 }
115
116 if (actionName != null)
117 {
118 actionMapper.put(actionName, "/" + actionName + "." + extension);
119 rolesMap.put(actionName, actionRolesRequired);
120 actionMapper.put(actionName + "!*", "/" + actionName + "!*." + extension);
121 rolesMap.put(actionName + "!*", actionRolesRequired);
122 }
123 }
124
125
126 NodeList commands = action.getElementsByTagName("command");
127 for (int j = 0; j < commands.getLength(); j++)
128 {
129 Element command = (Element) commands.item(j);
130 String cmdRolesRequired = overrideRoles(actionRolesRequired, command);
131
132 String commandAlias = command.getAttribute("alias");
133
134 if (commandAlias != null && cmdRolesRequired != null)
135 {
136 actionMapper.put(commandAlias, "/" + commandAlias + "." + extension);
137 rolesMap.put(commandAlias, cmdRolesRequired);
138 }
139 }
140 }
141 }
142
143 private org.w3c.dom.Document parseActionsXmlFile(String actionsXmlFile)
144 {
145 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
146 URL fileUrl = ClassLoaderUtil.getResource(actionsXmlFile + ".xml", this.getClass());
147
148 if (fileUrl == null)
149 fileUrl = ClassLoaderUtil.getResource("/" + actionsXmlFile + ".xml", this.getClass());
150
151 if (fileUrl == null)
152 throw new IllegalArgumentException("No such XML file:/" + actionsXmlFile + ".xml");
153
154 try
155 {
156 return factory.newDocumentBuilder().parse(fileUrl.toString());
157 }
158 catch (SAXException e)
159 {
160 throw new RuntimeException(e);
161 }
162 catch (IOException e)
163 {
164 throw new RuntimeException(e);
165 }
166 catch (ParserConfigurationException e)
167 {
168 throw new RuntimeException(e);
169 }
170 }
171
172
173
174
175 private String overrideRoles(String rolesRequired, Element action)
176 {
177 if (action.hasAttribute(ROLES_REQUIRED_ATTR))
178 {
179 return action.getAttribute(ROLES_REQUIRED_ATTR);
180 }
181 else
182 {
183 return rolesRequired;
184 }
185 }
186
187 public void destroy()
188 {
189 }
190
191 public Set getRequiredRoles(HttpServletRequest request)
192 {
193 Set requiredRoles = new HashSet();
194
195 String currentURL = request.getRequestURI();
196
197 int lastSlash = currentURL.lastIndexOf('/');
198 String targetURL;
199
200
201 if (lastSlash > -1)
202 {
203 targetURL = currentURL.substring(lastSlash);
204 }
205 else
206 {
207 targetURL = currentURL;
208 }
209
210 String actionMatch = actionMapper.get(targetURL);
211
212 if (actionMatch != null)
213 {
214 String rolesStr = (String) rolesMap.get(actionMatch);
215
216 StringTokenizer st = new StringTokenizer(rolesStr, ", ");
217 while (st.hasMoreTokens())
218 {
219 requiredRoles.add(st.nextToken());
220 }
221 }
222
223 return requiredRoles;
224 }
225 }