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.elevatedsecurity.ElevatedSecurityGuard;
12 import com.atlassian.seraph.elevatedsecurity.NoopElevatedSecurityGuard;
13 import com.atlassian.seraph.interceptor.Interceptor;
14 import com.atlassian.seraph.util.XMLUtils;
15 import com.opensymphony.util.ClassLoaderUtil;
16 import org.apache.log4j.Logger;
17 import org.w3c.dom.Document;
18 import org.w3c.dom.Element;
19 import org.w3c.dom.Node;
20 import org.w3c.dom.NodeList;
21
22 import java.io.Serializable;
23 import java.net.URL;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.CopyOnWriteArrayList;
30 import javax.xml.parsers.DocumentBuilderFactory;
31
32
33
34
35
36
37 public class SecurityConfigImpl implements Serializable, SecurityConfig
38 {
39 private static final Logger log = Logger.getLogger(SecurityConfigImpl.class);
40
41 public static final String DEFAULT_CONFIG_LOCATION = "seraph-config.xml";
42
43 private static final int YEAR_IN_SECONDS = 365 * 24 * 60 * 60;
44
45 private final Authenticator authenticator;
46 private final ElevatedSecurityGuard elevatedSecurityGuard;
47 private final RoleMapper roleMapper;
48 private final SecurityController controller;
49 private final List<SecurityService> services;
50 private final List<Interceptor> interceptors = new CopyOnWriteArrayList<Interceptor>();
51
52 private final String loginURL;
53 private final String logoutURL;
54 private final String originalURLKey;
55 private final String cookieEncoding;
56 private final String loginCookieKey;
57 private final String linkLoginURL;
58 private final String authType;
59 private RedirectPolicy redirectPolicy;
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, 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<String, String> globalParams = getInitParameters(parametersEl);
103
104 loginURL = globalParams.get("login.url");
105 linkLoginURL = globalParams.get("link.login.url");
106 logoutURL = globalParams.get("logout.url");
107 cookieEncoding = globalParams.get("cookie.encoding");
108 loginCookiePath = globalParams.get("login.cookie.path");
109 authType = globalParams.get("authentication.type");
110 insecureCookie = "true".equals(globalParams.get("insecure.cookie"));
111
112 if (globalParams.get("original.url.key") != null)
113 {
114 originalURLKey = 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 = 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"));
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 configureRedirectPolicy(rootEl);
149 CookieFactory.init(this);
150 elevatedSecurityGuard = configureElevatedSecurityGuard(rootEl);
151 }
152 catch (final Exception e)
153 {
154 throw new ConfigurationException("Exception configuring from '" + configFileLocation, e);
155 }
156 }
157
158 protected void configureRedirectPolicy(final Element rootEl) throws ConfigurationException
159 {
160
161 redirectPolicy = (RedirectPolicy) configureClass(rootEl, "redirect-policy", this);
162
163
164 if (redirectPolicy == null)
165 {
166 redirectPolicy = new DefaultRedirectPolicy();
167 }
168 }
169
170 private LoginUrlStrategy configureLoginUrlStrategy(final Element rootEl) throws ConfigurationException
171 {
172 LoginUrlStrategy loginUrlStrategy = (LoginUrlStrategy) configureClass(rootEl, "login-url-strategy", this);
173
174 if (loginUrlStrategy == null)
175 {
176 loginUrlStrategy = new DefaultLoginUrlStrategy();
177 }
178 return loginUrlStrategy;
179 }
180
181 private Authenticator configureAuthenticator(final Element rootEl) throws ConfigurationException
182 {
183 Authenticator authenticator = (Authenticator) configureClass(rootEl, "authenticator", this);
184
185 try
186 {
187 if (authenticator == null)
188 {
189 authenticator = (Authenticator) ClassLoaderUtil.loadClass(Authenticator.DEFAULT_AUTHENTICATOR, getClass()).newInstance();
190 authenticator.init(Collections.<String, String> emptyMap(), this);
191 }
192 }
193 catch (final Exception e)
194 {
195 throw new ConfigurationException("Could not lookup class: " + Authenticator.DEFAULT_AUTHENTICATOR, e);
196 }
197 return authenticator;
198 }
199
200 private ElevatedSecurityGuard configureElevatedSecurityGuard(final Element rootEl) throws ConfigurationException
201 {
202 ElevatedSecurityGuard elevatedSecurityGuard = (ElevatedSecurityGuard) configureClass(rootEl, "elevatedsecurityguard", this);
203 if (elevatedSecurityGuard == null)
204 {
205 elevatedSecurityGuard = NoopElevatedSecurityGuard.INSTANCE;
206 }
207 return elevatedSecurityGuard;
208 }
209
210 private SecurityController configureController(final Element rootEl) throws ConfigurationException
211 {
212 SecurityController controller = (SecurityController) configureClass(rootEl, "controller", this);
213
214 try
215 {
216 if (controller == null)
217 {
218 controller = (SecurityController) ClassLoaderUtil.loadClass(SecurityController.NULL_CONTROLLER, getClass()).newInstance();
219 }
220 }
221 catch (final Exception e)
222 {
223 throw new ConfigurationException("Could not lookup class: " + SecurityController.NULL_CONTROLLER, e);
224 }
225 return controller;
226 }
227
228 private RoleMapper configureRoleMapper(final Element rootEl) throws ConfigurationException
229 {
230 return (RoleMapper) configureClass(rootEl, "rolemapper", this);
231 }
232
233 private static Initable configureClass(final Element rootEl, final String tagname, final SecurityConfig owner) throws ConfigurationException
234 {
235 try
236 {
237 final NodeList elementList = rootEl.getElementsByTagName(tagname);
238
239 for (int i = 0; i < elementList.getLength(); i++)
240 {
241 final Element authEl = (Element) elementList.item(i);
242 final String clazz = authEl.getAttribute("class");
243
244 final Initable initable = (Initable) ClassLoaderUtil.loadClass(clazz, owner.getClass()).newInstance();
245
246 initable.init(getInitParameters(authEl), owner);
247 return initable;
248 }
249 return null;
250 }
251 catch (final Exception e)
252 {
253 throw new ConfigurationException("Could not create: " + tagname, e);
254 }
255 }
256
257
258 private List<SecurityService> configureServices(final Element rootEl) throws ConfigurationException
259 {
260 final NodeList nl = rootEl.getElementsByTagName("services");
261 final List<SecurityService> result = new ArrayList<SecurityService>();
262
263 if ((nl != null) && (nl.getLength() > 0))
264 {
265 final Element servicesEl = (Element) nl.item(0);
266 final NodeList serviceList = servicesEl.getElementsByTagName("service");
267
268 for (int i = 0; i < serviceList.getLength(); i++)
269 {
270 final Element serviceEl = (Element) serviceList.item(i);
271 final String serviceClazz = serviceEl.getAttribute("class");
272
273 if ((serviceClazz == null) || "".equals(serviceClazz))
274 {
275 throw new ConfigurationException("Service element with bad class attribute");
276 }
277
278 try
279 {
280 SecurityConfigImpl.log.debug("Adding seraph service of class: " + serviceClazz);
281 final SecurityService service = (SecurityService) ClassLoaderUtil.loadClass(serviceClazz, getClass()).newInstance();
282
283 service.init(getInitParameters(serviceEl), this);
284
285 result.add(service);
286 }
287 catch (final Exception e)
288 {
289 throw new ConfigurationException("Could not getRequest service: " + serviceClazz, e);
290 }
291 }
292 }
293 return result;
294 }
295
296 private void configureInterceptors(final Element rootEl) throws ConfigurationException
297 {
298 final NodeList nl = rootEl.getElementsByTagName("interceptors");
299
300 if ((nl != null) && (nl.getLength() > 0))
301 {
302 final Element interceptorsEl = (Element) nl.item(0);
303 final NodeList interceptorList = interceptorsEl.getElementsByTagName("interceptor");
304
305 for (int i = 0; i < interceptorList.getLength(); i++)
306 {
307 final Element interceptorEl = (Element) interceptorList.item(i);
308 final String interceptorClazz = interceptorEl.getAttribute("class");
309
310 if ((interceptorClazz == null) || "".equals(interceptorClazz))
311 {
312 throw new ConfigurationException("Interceptor element with bad class attribute");
313 }
314
315 try
316 {
317 SecurityConfigImpl.log.debug("Adding interceptor of class: " + interceptorClazz);
318 final Interceptor interceptor = (Interceptor) ClassLoaderUtil.loadClass(interceptorClazz, getClass()).newInstance();
319
320 interceptor.init(getInitParameters(interceptorEl), this);
321
322 interceptors.add(interceptor);
323 }
324 catch (final Exception e)
325 {
326 throw new ConfigurationException("Could not getRequest service: " + interceptorClazz, e);
327 }
328 }
329 }
330 }
331
332
333
334
335
336
337
338
339 private static Map<String, String> getInitParameters(final Element el)
340 {
341 final Map<String, String> params = new HashMap<String, String>();
342 final NodeList nl = el.getElementsByTagName("init-param");
343
344 for (int i = 0; i < nl.getLength(); i++)
345 {
346 final Node initParam = nl.item(i);
347 final String paramName = XMLUtils.getContainedText(initParam, "param-name");
348 final String paramValue = XMLUtils.getContainedText(initParam, "param-value");
349 params.put(paramName, paramValue);
350 }
351 return Collections.unmodifiableMap(params);
352 }
353
354 public void destroy()
355 {
356 for (final Object element : services)
357 {
358 final SecurityService securityService = (SecurityService) element;
359 securityService.destroy();
360 }
361
362 for (final Object element : interceptors)
363 {
364 ((Interceptor) element).destroy();
365 }
366 }
367
368
369
370
371
372 public void addInterceptor(final Interceptor interceptor)
373 {
374 interceptors.add(interceptor);
375 }
376
377 public List<SecurityService> getServices()
378 {
379 return services;
380 }
381
382 public String getLoginURL()
383 {
384 return loginUrlStrategy.getLoginURL(this, loginURL);
385 }
386
387 public String getLinkLoginURL()
388 {
389 return loginUrlStrategy.getLinkLoginURL(this, linkLoginURL);
390 }
391
392 public String getLogoutURL()
393 {
394 return loginUrlStrategy.getLogoutURL(this, logoutURL);
395 }
396
397 public String getOriginalURLKey()
398 {
399 return originalURLKey;
400 }
401
402 public Authenticator getAuthenticator()
403 {
404 return authenticator;
405 }
406
407 public AuthenticationContext getAuthenticationContext()
408 {
409 return new AuthenticationContextImpl();
410 }
411
412 public SecurityController getController()
413 {
414 return controller;
415 }
416
417 public RoleMapper getRoleMapper()
418 {
419 return roleMapper;
420 }
421
422 public RedirectPolicy getRedirectPolicy()
423 {
424 return redirectPolicy;
425 }
426
427 public <T extends Interceptor> List<T> getInterceptors(final Class<T> desiredInterceptorClass)
428 {
429 final List<T> result = new ArrayList<T>();
430 for (final Interceptor interceptor : interceptors)
431 {
432 if (desiredInterceptorClass.isAssignableFrom(interceptor.getClass()))
433 {
434 result.add(desiredInterceptorClass.cast(interceptor));
435 }
436 }
437 return Collections.unmodifiableList(result);
438 }
439
440 public String getCookieEncoding()
441 {
442 return cookieEncoding;
443 }
444
445 public String getLoginCookiePath()
446 {
447 return loginCookiePath;
448 }
449
450 public String getLoginCookieKey()
451 {
452 return loginCookieKey;
453 }
454
455 public String getAuthType()
456 {
457 return authType;
458 }
459
460 public boolean isInsecureCookie()
461 {
462 return insecureCookie;
463 }
464
465 public int getAutoLoginCookieAge()
466 {
467 return autoLoginCookieAge;
468 }
469
470 public ElevatedSecurityGuard getElevatedSecurityGuard()
471 {
472 return elevatedSecurityGuard;
473 }
474 }