View Javadoc

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