View Javadoc

1   /*
2    * Copyright 2000-2001,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.jetspeed.services.security;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import javax.servlet.ServletConfig;
25  
26  import org.apache.jetspeed.om.security.JetspeedUser;
27  import org.apache.jetspeed.om.security.JetspeedUserFactory;
28  import org.apache.jetspeed.om.security.UserNamePrincipal;
29  import org.apache.jetspeed.portal.Portlet;
30  import org.apache.jetspeed.services.JetspeedPortalAccessController;
31  import org.apache.jetspeed.services.JetspeedSecurity;
32  import org.apache.jetspeed.services.JetspeedUserManagement;
33  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
34  import org.apache.jetspeed.services.logging.JetspeedLogger;
35  import org.apache.jetspeed.services.rundata.JetspeedRunData;
36  import org.apache.turbine.om.security.User;
37  import org.apache.turbine.services.InitializationException;
38  import org.apache.turbine.services.TurbineBaseService;
39  import org.apache.turbine.services.TurbineServices;
40  import org.apache.turbine.services.resources.ResourceService;
41  
42  /***
43   * <p>This is an implementation of the <code>JetspeedSecurityService</code> interface.
44   *
45   *
46   * @author <a href="mailto:david@bluesunrise.com">David Sean Taylor</a>
47   * @author <a href="mailto:sgala@hisitech.com">Santiago Gala</a>
48   * @version $Id: JetspeedDBSecurityService.java,v 1.25 2004/03/31 04:49:10 morciuch Exp $
49   */
50  
51  public class JetspeedDBSecurityService extends TurbineBaseService
52                                         implements JetspeedSecurityService
53  {
54      /***
55       * Static initialization of the logger for this class
56       */    
57      private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedDBSecurityService.class.getName());
58      
59      private final static String CONFIG_CASEINSENSITIVE_USERNAME = "caseinsensitive.username";
60      private final static String CONFIG_CASEINSENSITIVE_PASSWORD = "caseinsensitive.password";
61      private final static String CONFIG_CASEINSENSITIVE_UPPER = "caseinsensitive.upper";
62      private final static String CONFIG_LOGON_STRIKE_COUNT = "logon.strike.count";
63      private final static String CONFIG_LOGON_STRIKE_MAX = "logon.strike.max";
64      private final static String CONFIG_LOGON_STRIKE_INTERVAL = "logon.strike.interval";
65      private final static String CONFIG_LOGON_AUTO_DISABLE = "logon.auto.disable";
66      private final static String CONFIG_ACTIONS_ANON_DISABLE = "actions.anon.disable";
67      private final static String CONFIG_ACTIONS_ALLUSERS_DISABLE = "actions.allusers.disable";
68  	private final static String CONFIG_ACTIONS_ADMIN_ROLES = "admin.roles";
69  
70      private final static String CONFIG_NEWUSER_ROLES     = "newuser.roles";
71      private final static String CONFIG_DEFAULT_PERMISSION_LOGGEDIN     = "permission.default.loggedin";
72      private final static String CONFIG_DEFAULT_PERMISSION_ANONYMOUS     = "permission.default.anonymous";
73      private final static String CONFIG_ANONYMOUS_USER = "user.anonymous";
74      private final static String [] DEFAULT_PERMISSIONS = {""};
75      private final static String [] DEFAULT_CONFIG_NEWUSER_ROLES = 
76      { "user" };
77  	private final static String [] DEFAULT_ADMIN_ROLES = 
78  	{ "admin" };
79  
80      String roles[] = null;
81      boolean caseInsensitiveUsername = false;
82      boolean caseInsensitivePassword = false;
83      boolean caseInsensitiveUpper = true;
84      boolean actionsAnonDisable = true;
85      boolean actionsAllUsersDisable = false;
86      String anonymousUser = "anon";
87  	String[] adminRoles = null;
88  
89      int strikeCount = 3;             // 3 within the interval
90      int strikeMax = 20;              // 20 total failures 
91      long strikeInterval = 300;  // five minutes
92  
93      boolean autoLogonDisable = false;
94  
95      private static HashMap users = new HashMap();
96  
97      private static Object sem = new Object();
98  
99      /***
100      * This is the early initialization method called by the 
101      * Turbine <code>Service</code> framework
102      * @param conf The <code>ServletConfig</code>
103      * @exception throws a <code>InitializationException</code> if the service
104      * fails to initialize
105      */
106     public synchronized void init(ServletConfig conf) throws InitializationException 
107     {
108         // already initialized
109         if (getInit()) return;
110 
111         super.init(conf);
112 
113         // get configuration parameters from Jetspeed Resources
114         ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
115                                                      .getResources(JetspeedSecurityService.SERVICE_NAME);
116         
117         try
118         {
119             roles = serviceConf.getStringArray(CONFIG_NEWUSER_ROLES);
120 			adminRoles = serviceConf.getStringArray(CONFIG_ACTIONS_ADMIN_ROLES);
121         }
122         catch (Exception e)
123         {}
124             
125         if (null == roles || roles.length == 0)
126         {
127             roles = DEFAULT_CONFIG_NEWUSER_ROLES;
128         }
129 
130 		if (null == adminRoles || adminRoles.length == 0)
131 		{
132 			adminRoles = DEFAULT_ADMIN_ROLES;
133 		}
134 
135         caseInsensitiveUsername = serviceConf.getBoolean(CONFIG_CASEINSENSITIVE_USERNAME, caseInsensitiveUsername);
136         caseInsensitivePassword = serviceConf.getBoolean(CONFIG_CASEINSENSITIVE_PASSWORD, caseInsensitivePassword);
137         caseInsensitiveUpper = serviceConf.getBoolean(CONFIG_CASEINSENSITIVE_UPPER, caseInsensitiveUpper);
138 
139         strikeCount = serviceConf.getInt(CONFIG_LOGON_STRIKE_COUNT, strikeCount);
140         strikeInterval = serviceConf.getLong(CONFIG_LOGON_STRIKE_INTERVAL, strikeInterval);
141         strikeMax = serviceConf.getInt(CONFIG_LOGON_STRIKE_MAX, strikeMax);
142 
143         autoLogonDisable = serviceConf.getBoolean(CONFIG_LOGON_AUTO_DISABLE, autoLogonDisable);
144         actionsAnonDisable = serviceConf.getBoolean(CONFIG_ACTIONS_ANON_DISABLE, actionsAnonDisable);
145         actionsAllUsersDisable = serviceConf.getBoolean(CONFIG_ACTIONS_ALLUSERS_DISABLE, actionsAllUsersDisable);
146 
147         anonymousUser = serviceConf.getString(CONFIG_ANONYMOUS_USER, anonymousUser);
148 
149         // initialization done
150         setInit(true);
151      }
152 
153 
154     //////////////////////////////////////////////////////////////////////////
155     //
156     // Required JetspeedSecurity Functions
157     //
158     // Required Features provided by default JetspeedSecurity
159     //
160     //////////////////////////////////////////////////////////////////////////
161 
162     /*
163      * Factory to create a new JetspeedUser, using JetspeedUserFactory.
164      * The class that is created by the default JetspeedUserFactory is configured
165      * in the JetspeedSecurity properties:
166      *
167      *    services.JetspeedSecurity.user.class=
168      *        org.apache.jetspeed.om.security.BaseJetspeedUser
169      *
170      * @return JetspeedUser a newly created user that implements JetspeedUser.
171      */
172     public JetspeedUser getUserInstance()
173     {
174         try
175         {
176             return JetspeedUserFactory.getInstance();
177         }
178         catch (UserException e)
179         {
180             return null;
181         }
182     }
183 
184     //////////////////////////////////////////////////////////////////////////
185     //
186     // Optional JetspeedSecurity Features 
187     //
188     // Features are not required to be implemented by Security Provider
189     //
190     //////////////////////////////////////////////////////////////////////////
191 
192     /*
193      * During logon, the username can be case sensitive or case insensitive.
194      *
195      * Given a username, converts the username to either lower or upper case.
196      * This optional feature is configurable from the JetspeedSecurity.properties:
197      *
198      *     <code>services.JetspeedSecurity.caseinsensitive.username = true/false</code>
199      *     <code>services.JetspeedSecurity.caseinsensitive.upper = true/false</code>
200      *
201      * If <code>caseinsensitive.username</code> is true,  
202      * then conversion is enabled and the username will be converted before 
203      * being sent to the Authentication provider.
204      *
205      * @param username The username to be converted depending on configuration.
206      * @return The converted username.
207      *
208      */
209     public String convertUserName(String username)
210     {
211         if (caseInsensitiveUsername)
212         { 
213             username = (caseInsensitiveUpper) ? username.toUpperCase() : username.toLowerCase(); 
214         } 
215         return username;
216     }
217 
218     /*
219      * During logon, the password can be case sensitive or case insensitive.
220      *
221      * Given a password, converts the password to either lower or upper case.
222      * This optional feature is configurable from the JetspeedSecurity.properties:
223      *
224      *     <code>services.JetspeedSecurity.caseinsensitive.password = true/false</code>
225      *     <code>services.JetspeedSecurity.caseinsensitive.upper = true/false</code>
226      *
227      * If <code>caseinsensitive.password</code> is true,  
228      * then conversion is enabled and the password will be converted before 
229      * being sent to the Authentication provider.
230      *
231      * @param password The password to be converted depending on configuration.
232      * @return The converted password.
233      *
234      */
235     public String convertPassword(String password)
236     {
237         if (caseInsensitivePassword)
238         { 
239             password = (caseInsensitiveUpper) ? password.toUpperCase() : password.toLowerCase(); 
240         } 
241         return password;
242     }
243 
244     /*
245      * Logon Failure / Account Disabling Feature
246      *
247      * Checks and tracks failed user-logon attempts.
248      * If the user fails to logon after a configurable number of logon attempts,
249      * then the user's account will be disabled.
250      *
251      * This optional feature is configurable from the JetspeedSecurity.properties:
252      *
253      *     <code>services.JetspeedSecurity.logon.auto.disable=false</code>
254      *
255      * The example setting below allows for 3 logon strikes per 300 seconds.
256      * When the strike.count is exceeded over the strike.interval, the account
257      * is disabled. The strike.max is the cumulative maximum.
258      *
259      *     <code>services.JetspeedSecurity.logon.strike.count=3</code>
260      *     <code>services.JetspeedSecurity.logon.strike.interval=300</code>
261      *     <code>services.JetspeedSecurity.logon.strike.max=10</code>
262      *
263      * These settings are not persisted, and in a distributed environment are 
264      * only tracked per node.
265      *
266      * @param username The username to be checked.
267      * @return True if the strike count reached the maximum threshold and the
268      *         user's account was disabled, otherwise False.
269      *
270      */
271     public boolean checkDisableAccount(String username)
272     {
273         username = convertUserName(username);
274  
275         // TODO: make this work across a cluster of servers
276         UserLogonStats stat = (UserLogonStats)users.get(username);
277         if (stat == null)
278         {
279             stat = new UserLogonStats(username);
280             synchronized (sem)
281             {
282                 users.put(username, stat);
283             }
284         }
285         boolean disabled = stat.failCheck(strikeCount, strikeInterval, strikeMax);
286 
287         if (disabled)
288         {
289             try
290             {
291                 // disable the account
292                 JetspeedUser user = (JetspeedUser)JetspeedSecurity.getUser(username);
293                 if (user != null)
294                 {
295                     user.setDisabled(true);
296                     JetspeedSecurity.saveUser(user);
297                 }
298             }
299             catch (Exception e)
300             {
301                  logger.error("Could not disable user: " + username, e);
302             }
303         }
304         return disabled;
305     }
306 
307     /*
308      * Logon Failure / Account Disabling Feature
309      *    
310      * Returns state of the the logon failure / account disabling feature.
311      * 
312      * If the user fails to logon after a configurable number of logon attempts,
313      * then the user's account will be disabled.
314      *
315      * @see JetspeedSecurityService#checkLogonFailures
316      *
317      * @return True if the feature is enabled, false if the feature is disabled.
318      *
319      */
320     public boolean isDisableAccountCheckEnabled()
321     {
322         return autoLogonDisable;
323     }
324 
325     
326     /*
327      * Logon Failure / Account Disabling Feature
328      *    
329      * Resets counters for the logon failure / account disabling feature.
330      * 
331      * If the user fails to logon after a configurable number of logon attempts,
332      * then the user's account will be disabled.
333      *
334      * @see JetspeedSecurityService#checkLogonFailures
335      *
336      * @param username The username to reset the logon failure counters.
337      *
338      */
339     public void resetDisableAccountCheck(String username)
340     {
341         // TODO: make this work across a cluster of servers
342         username = convertUserName(username);
343         UserLogonStats stat = (UserLogonStats)users.get(username);
344         if (stat == null)           
345         {
346             stat = new UserLogonStats(username);
347             synchronized (sem)
348             {
349                 users.put(username, stat);
350             }
351         }
352         stat.reset();
353     }
354     
355 
356     //////////////////////////////////////////////////////////////////////////
357     //
358     // Optional JetspeedSecurity Helpers
359     //
360     //////////////////////////////////////////////////////////////////////////
361 
362     /***
363      * Helper to UserManagement.
364      * Retrieves a <code>JetspeedUser</code> given the primary principle username.
365      * The principal can be any valid Jetspeed Security Principal:
366      *   <code>org.apache.jetspeed.om.security.UserNamePrincipal</code>
367      *   <code>org.apache.jetspeed.om.security.UserIdPrincipal</code>
368      *   
369      * The security service may optionally check the current user context
370      * to determine if the requestor has permission to perform this action.
371      *
372      * @param username The username principal.
373      * @return a <code>JetspeedUser</code> associated to the principal identity.
374      * @exception UserException when the security provider has a general failure retrieving a user.
375      * @exception UnknownUserException when the security provider cannot match
376      *            the principal identity to a user.
377      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege 
378      */
379 
380     public JetspeedUser getUser(String username) 
381         throws JetspeedSecurityException
382     {
383         return JetspeedUserManagement.getUser(new UserNamePrincipal(username));
384     }
385 
386 
387     /***
388      * Helper to PortalAuthorization.
389      * Gets a <code>JetspeedUser</code> from rundata, authorize user to perform the secured action on
390      * the given <code>Portlet</code> resource. If the user does not have
391      * sufficient privilege to perform the action on the resource, the check returns false,
392      * otherwise when sufficient privilege is present, checkPermission returns true.
393      *
394      * @param rundata request that the user is taken from rundatas
395      * @param action the secured action to be performed on the resource by the user.     
396      * @param portlet the portlet resource.
397      * @return boolean true if the user has sufficient privilege.
398      */
399     public boolean checkPermission(JetspeedRunData runData, String action, Portlet portlet)
400     {
401         return JetspeedPortalAccessController.checkPermission(runData.getJetspeedUser(),
402                                                        portlet,
403                                                        action);
404     }
405 
406     /***
407      * Helper to PortalAuthorization.
408      * Gets a <code>JetspeedUser</code> from rundata, authorize user to perform the secured action on
409      * the given <code>Entry</code> resource. If the user does not have
410      * sufficient privilege to perform the action on the resource, the check returns false,
411      * otherwise when sufficient privilege is present, checkPermission returns true.
412      *
413      * @param rundata request that the user is taken from rundatas
414      * @param action the secured action to be performed on the resource by the user.     
415      * @param entry the portal entry resource.
416      * @return boolean true if the user has sufficient privilege.
417     public boolean checkPermission(JetspeedRunData runData, String action, RegistryEntry entry)
418     {
419         return JetspeedPortalAccessController.checkPermission(runData.getJetspeedUser(),
420                                                        entry,
421                                                        action);
422     }
423      */
424 
425     /*
426      * Security configuration setting to disable all action buttons for the Anon user
427      * This setting is readonly and is edited in the JetspeedSecurity deployment
428      *    
429      *
430      * @return True if the feature actions are disabled for the anon user
431      *
432      */
433     public boolean areActionsDisabledForAnon()
434     {
435         return actionsAnonDisable;
436     }
437 
438     /*
439      * Security configuration setting to disable all action buttons for all users
440      * This setting is readonly and is edited in the JetspeedSecurity deployment
441      *    
442      *
443      * @return True if the feature actions are disabled for the all users
444      *
445      */
446     public boolean areActionsDisabledForAllUsers()
447     {
448         return actionsAllUsersDisable;
449     }
450 
451    /*
452      * Gets the name of the anonymous user account if applicable
453      *    
454      *
455      * @return String the name of the anonymous user account
456      *
457      */
458     public String getAnonymousUserName()
459     {
460         return anonymousUser;
461     }
462 
463 	/*
464 	 * Gets the list of administrative roles
465 	 *    
466 	 * @return list of admin roles
467 	 */
468 	 public List getAdminRoles()
469 	 {
470 	 	List result = new ArrayList();
471 	 	for (int i = 0; i < adminRoles.length; i++)
472 	 	{
473 	 		result.add(adminRoles[i]);
474 	 	}
475 	 	
476 		return result;
477 	 }
478 
479 	/***
480 	 * Returns true if user has administrative role
481 	 * 
482 	 * @param user
483 	 * @return true if user has administrative role
484 	 */
485 	public boolean hasAdminRole(User user)
486 	{
487 		String username = user.getUserName();
488 		try
489 		{
490 			List adminRoles = getAdminRoles();
491 			for (Iterator it = adminRoles.iterator(); it.hasNext();)
492 			{
493 				if (JetspeedSecurity.hasRole(username, (String)it.next()))
494 				{
495 					return true;
496 				}
497 			}
498 		}
499 		catch (Exception e)
500 		{	
501 			logger.error(e);		
502 		}
503 		
504 		return false;
505 	}
506 
507 }
508