View Javadoc

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   * The main class of Seraph's configuration.
36   * <p>
37   * This class is a Singleton, access it using SecurityConfigFactory.getInstance().
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      // the age of the auto-login cookie - default = 1 year (in seconds)
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              // Parse document
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             // be VERY careful about changing order here, THIS reference is passed out while initializing and so we are partially constructed when
140             // clients call us
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     // only called from the constructor
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      * Do not use in production! Only used in tests, will be removed.
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(); // todo - load this from a config file
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 }