1 package com.atlassian.seraph.config;
2
3 import com.atlassian.seraph.Initable;
4 import com.atlassian.seraph.SecurityService;
5 import com.atlassian.seraph.auth.AuthenticationContext;
6 import com.atlassian.seraph.auth.AuthenticationContextImpl;
7 import com.atlassian.seraph.auth.Authenticator;
8 import com.atlassian.seraph.auth.RoleMapper;
9 import com.atlassian.seraph.controller.SecurityController;
10 import com.atlassian.seraph.cookie.CookieFactory;
11 import com.atlassian.seraph.interceptor.Interceptor;
12 import com.atlassian.seraph.util.XMLUtils;
13
14 import org.apache.log4j.Logger;
15 import org.w3c.dom.Document;
16 import org.w3c.dom.Element;
17 import org.w3c.dom.Node;
18 import org.w3c.dom.NodeList;
19
20 import com.opensymphony.util.ClassLoaderUtil;
21
22 import edu.emory.mathcs.backport.java.util.Collections;
23
24 import java.io.Serializable;
25 import java.net.URL;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31
32 import javax.xml.parsers.DocumentBuilderFactory;
33
34
35
36
37
38
39 public class SecurityConfigImpl implements Serializable, SecurityConfig
40 {
41 private static final Logger log = Logger.getLogger(SecurityConfigImpl.class);
42
43 public static final String DEFAULT_CONFIG_LOCATION = "seraph-config.xml";
44
45 private static final int YEAR_IN_SECONDS = 365 * 24 * 60 * 60;
46
47 private final Authenticator authenticator;
48 private final RoleMapper roleMapper;
49 private final SecurityController controller;
50 private final List services;
51 private final List interceptors = Collections.synchronizedList(new ArrayList());
52
53 private final String loginURL;
54 private final String logoutURL;
55 private final String originalURLKey;
56 private final String cookieEncoding;
57 private final String loginCookieKey;
58 private final String linkLoginURL;
59 private final String authType;
60
61 private boolean insecureCookie;
62
63 private final int autoLoginCookieAge;
64
65 private final LoginUrlStrategy loginUrlStrategy;
66 private final String loginCookiePath;
67
68 public SecurityConfigImpl(String configFileLocation) throws ConfigurationException
69 {
70 if (configFileLocation != null)
71 {
72 if (SecurityConfigImpl.log.isDebugEnabled())
73 {
74 SecurityConfigImpl.log.debug("Config file location passed. Location: " + configFileLocation);
75 }
76 }
77 else
78 {
79 configFileLocation = "seraph-config.xml";
80 if (SecurityConfigImpl.log.isDebugEnabled())
81 {
82 SecurityConfigImpl.log.debug("Initialising securityConfig using default configFile: " + configFileLocation);
83 }
84 }
85
86 try
87 {
88 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
89 final URL fileUrl = ClassLoaderUtil.getResource(configFileLocation, this.getClass());
90
91 if (fileUrl == null)
92 {
93 throw new IllegalArgumentException("No such XML file: " + configFileLocation);
94 }
95
96
97 final Document doc = factory.newDocumentBuilder().parse(fileUrl.toString());
98 final Element rootEl = doc.getDocumentElement();
99
100 final NodeList nl = rootEl.getElementsByTagName("parameters");
101 final Element parametersEl = (Element) nl.item(0);
102 final Map globalParams = getInitParameters(parametersEl);
103
104 loginURL = (String) globalParams.get("login.url");
105 linkLoginURL = (String) globalParams.get("link.login.url");
106 logoutURL = (String) globalParams.get("logout.url");
107 cookieEncoding = (String) globalParams.get("cookie.encoding");
108 loginCookiePath = (String) globalParams.get("login.cookie.path");
109 authType = (String) globalParams.get("authentication.type");
110 insecureCookie = "true".equals(globalParams.get("insecure.cookie"));
111
112 if (globalParams.get("original.url.key") != null)
113 {
114 originalURLKey = (String) globalParams.get("original.url.key");
115 }
116 else
117 {
118 originalURLKey = "seraph_originalurl";
119 }
120
121 if (globalParams.get("login.cookie.key") != null)
122 {
123 loginCookieKey = (String) globalParams.get("login.cookie.key");
124 }
125 else
126 {
127 loginCookieKey = "seraph.os.cookie";
128 }
129
130 if (globalParams.get("autologin.cookie.age") != null)
131 {
132 autoLoginCookieAge = Integer.parseInt(globalParams.get("autologin.cookie.age").toString());
133 }
134 else
135 {
136 autoLoginCookieAge = SecurityConfigImpl.YEAR_IN_SECONDS;
137 }
138
139
140
141
142 authenticator = configureAuthenticator(rootEl);
143 controller = configureController(rootEl);
144 roleMapper = configureRoleMapper(rootEl);
145 services = Collections.unmodifiableList(configureServices(rootEl));
146 configureInterceptors(rootEl);
147 loginUrlStrategy = configureLoginUrlStrategy(rootEl);
148 CookieFactory.init(this);
149 }
150 catch (final Exception e)
151 {
152 throw new ConfigurationException("Exception configuring from '" + configFileLocation, e);
153 }
154 }
155
156 private LoginUrlStrategy configureLoginUrlStrategy(final Element rootEl) throws ConfigurationException
157 {
158 LoginUrlStrategy loginUrlStrategy = (LoginUrlStrategy) configureClass(rootEl, "login-url-strategy", this);
159
160 if (loginUrlStrategy == null)
161 {
162 loginUrlStrategy = new DefaultLoginUrlStrategy();
163 }
164 return loginUrlStrategy;
165 }
166
167 private Authenticator configureAuthenticator(final Element rootEl) throws ConfigurationException
168 {
169 Authenticator authenticator = (Authenticator) configureClass(rootEl, "authenticator", this);
170
171 try
172 {
173 if (authenticator == null)
174 {
175 authenticator = (Authenticator) ClassLoaderUtil.loadClass(Authenticator.DEFAULT_AUTHENTICATOR, this.getClass()).newInstance();
176 authenticator.init(new HashMap(), this);
177 }
178 }
179 catch (final Exception e)
180 {
181 throw new ConfigurationException("Could not lookup class: " + Authenticator.DEFAULT_AUTHENTICATOR, e);
182 }
183 return authenticator;
184 }
185
186 private SecurityController configureController(final Element rootEl) throws ConfigurationException
187 {
188 SecurityController controller = (SecurityController) configureClass(rootEl, "controller", this);
189
190 try
191 {
192 if (controller == null)
193 {
194 controller = (SecurityController) ClassLoaderUtil.loadClass(SecurityController.NULL_CONTROLLER, this.getClass()).newInstance();
195 }
196 }
197 catch (final Exception e)
198 {
199 throw new ConfigurationException("Could not lookup class: " + SecurityController.NULL_CONTROLLER, e);
200 }
201 return controller;
202 }
203
204 private RoleMapper configureRoleMapper(final Element rootEl) throws ConfigurationException
205 {
206 return (RoleMapper) configureClass(rootEl, "rolemapper", this);
207 }
208
209 private static Initable configureClass(final Element rootEl, final String tagname, final SecurityConfig owner) throws ConfigurationException
210 {
211 try
212 {
213 final NodeList elementList = rootEl.getElementsByTagName(tagname);
214
215 for (int i = 0; i < elementList.getLength(); i++)
216 {
217 final Element authEl = (Element) elementList.item(i);
218 final String clazz = authEl.getAttribute("class");
219
220 final Initable initable = (Initable) ClassLoaderUtil.loadClass(clazz, owner.getClass()).newInstance();
221
222 final Map params = getInitParameters(authEl);
223
224 initable.init(params, owner);
225 return initable;
226 }
227 return null;
228 }
229 catch (final Exception e)
230 {
231 throw new ConfigurationException("Could not create: " + tagname, e);
232 }
233 }
234
235
236 private List configureServices(final Element rootEl) throws ConfigurationException
237 {
238 final NodeList nl = rootEl.getElementsByTagName("services");
239 final List result = new ArrayList();
240
241 if ((nl != null) && (nl.getLength() > 0))
242 {
243 final Element servicesEl = (Element) nl.item(0);
244 final NodeList serviceList = servicesEl.getElementsByTagName("service");
245
246 for (int i = 0; i < serviceList.getLength(); i++)
247 {
248 final Element serviceEl = (Element) serviceList.item(i);
249 final String serviceClazz = serviceEl.getAttribute("class");
250
251 if ((serviceClazz == null) || "".equals(serviceClazz))
252 {
253 throw new ConfigurationException("Service element with bad class attribute");
254 }
255
256 try
257 {
258 SecurityConfigImpl.log.debug("Adding seraph service of class: " + serviceClazz);
259 final SecurityService service = (SecurityService) ClassLoaderUtil.loadClass(serviceClazz, this.getClass()).newInstance();
260
261 final Map serviceParams = getInitParameters(serviceEl);
262
263 service.init(serviceParams, this);
264
265 result.add(service);
266 }
267 catch (final Exception e)
268 {
269 throw new ConfigurationException("Could not getRequest service: " + serviceClazz, e);
270 }
271 }
272 }
273 return result;
274 }
275
276 private void configureInterceptors(final Element rootEl) throws ConfigurationException
277 {
278 final NodeList nl = rootEl.getElementsByTagName("interceptors");
279
280 if ((nl != null) && (nl.getLength() > 0))
281 {
282 final Element interceptorsEl = (Element) nl.item(0);
283 final NodeList interceptorList = interceptorsEl.getElementsByTagName("interceptor");
284
285 for (int i = 0; i < interceptorList.getLength(); i++)
286 {
287 final Element interceptorEl = (Element) interceptorList.item(i);
288 final String interceptorClazz = interceptorEl.getAttribute("class");
289
290 if ((interceptorClazz == null) || "".equals(interceptorClazz))
291 {
292 throw new ConfigurationException("Interceptor element with bad class attribute");
293 }
294
295 try
296 {
297 SecurityConfigImpl.log.debug("Adding interceptor of class: " + interceptorClazz);
298 final Interceptor interceptor = (Interceptor) ClassLoaderUtil.loadClass(interceptorClazz, this.getClass()).newInstance();
299
300 final Map interceptorParams = getInitParameters(interceptorEl);
301
302 interceptor.init(interceptorParams, this);
303
304 interceptors.add(interceptor);
305 }
306 catch (final Exception e)
307 {
308 throw new ConfigurationException("Could not getRequest service: " + interceptorClazz, e);
309 }
310 }
311 }
312 }
313
314 private static Map getInitParameters(final Element el)
315 {
316 final Map params = new HashMap();
317 final NodeList nl = el.getElementsByTagName("init-param");
318
319 for (int i = 0; i < nl.getLength(); i++)
320 {
321 final Node initParam = nl.item(i);
322 final String paramName = XMLUtils.getContainedText(initParam, "param-name");
323 final String paramValue = XMLUtils.getContainedText(initParam, "param-value");
324 params.put(paramName, paramValue);
325 }
326 return Collections.unmodifiableMap(params);
327 }
328
329 public void destroy()
330 {
331 for (final Iterator iterator = services.iterator(); iterator.hasNext();)
332 {
333 final SecurityService securityService = (SecurityService) iterator.next();
334 securityService.destroy();
335 }
336
337 for (final Iterator iterator = interceptors.iterator(); iterator.hasNext();)
338 {
339 ((Interceptor) iterator.next()).destroy();
340 }
341 }
342
343
344
345
346 public void addInterceptor(final Interceptor interceptor)
347 {
348 interceptors.add(interceptor);
349 }
350
351 public List getServices()
352 {
353 return services;
354 }
355
356 public String getLoginURL()
357 {
358 return loginUrlStrategy.getLoginURL(this, loginURL);
359 }
360
361 public String getLinkLoginURL()
362 {
363 return loginUrlStrategy.getLinkLoginURL(this, linkLoginURL);
364 }
365
366 public String getLogoutURL()
367 {
368 return loginUrlStrategy.getLogoutURL(this, logoutURL);
369 }
370
371 public String getOriginalURLKey()
372 {
373 return originalURLKey;
374 }
375
376 public Authenticator getAuthenticator()
377 {
378 return authenticator;
379 }
380
381 public AuthenticationContext getAuthenticationContext()
382 {
383 return new AuthenticationContextImpl();
384 }
385
386 public SecurityController getController()
387 {
388 return controller;
389 }
390
391 public RoleMapper getRoleMapper()
392 {
393 return roleMapper;
394 }
395
396 public List getInterceptors(final Class desiredInterceptorClass)
397 {
398 final List result = new ArrayList();
399
400 for (final Iterator iterator = interceptors.iterator(); iterator.hasNext();)
401 {
402 final Interceptor interceptor = (Interceptor) iterator.next();
403
404 if (desiredInterceptorClass.isAssignableFrom(interceptor.getClass()))
405 {
406 result.add(interceptor);
407 }
408 }
409
410 return Collections.unmodifiableList(result);
411 }
412
413 public String getCookieEncoding()
414 {
415 return cookieEncoding;
416 }
417
418 public String getLoginCookiePath()
419 {
420 return loginCookiePath;
421 }
422
423 public String getLoginCookieKey()
424 {
425 return loginCookieKey;
426 }
427
428 public String getAuthType()
429 {
430 return authType;
431 }
432
433 public boolean isInsecureCookie()
434 {
435 return insecureCookie;
436 }
437
438 public int getAutoLoginCookieAge()
439 {
440 return autoLoginCookieAge;
441 }
442 }